2 Copyright 2020 TATA ELXSI
4 Licensed under the Apache License, Version 2.0 (the 'License');
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
16 Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
19 * @file VNFComposerComponent
21 import { HttpHeaders } from '@angular/common/http';
22 import { Component, ElementRef, Injector, ViewChild, ViewEncapsulation } from '@angular/core';
23 import { ActivatedRoute, Router } from '@angular/router';
24 import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
25 import { TranslateService } from '@ngx-translate/core';
26 import { NotifierService } from 'angular-notifier';
27 import { APIURLHEADER, ERRORDATA, GETAPIURLHEADER, MODALCLOSERESPONSEDATA } from 'CommonModel';
28 import { ConfirmationTopologyComponent } from 'ConfirmationTopology';
29 import * as d3 from 'd3';
30 import { DataService } from 'DataService';
31 import { environment } from 'environment';
32 import * as HttpStatus from 'http-status-codes';
33 import * as jsyaml from 'js-yaml';
34 import { RestService } from 'RestService';
35 import { SharedService } from 'SharedService';
36 import { isNullOrUndefined } from 'util';
38 COMPOSERNODES, CONNECTIONPOINT, GRAPHDETAILS, InternalVLD, Tick, TickPath,
39 VDU, VDUInternalConnectionPoint, VLDInternalConnectionPoint, VNFDInterface, VNFDNODE
44 * @Component takes VNFComposerComponent.html as template url
47 templateUrl: './VNFComposerComponent.html',
48 styleUrls: ['./VNFComposerComponent.scss'],
49 encapsulation: ViewEncapsulation.None
51 /** Exporting a class @exports VNFComposerComponent */
52 export class VNFComposerComponent {
53 /** To inject services @public */
54 public injector: Injector;
55 /** View child contains graphContainer ref @public */
56 @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef;
57 /** dataService to pass the data from one component to another @public */
58 public dataService: DataService;
59 /** random number count @public */
60 public randomNumberLength: number;
61 /** Contains the vnfd information @public */
62 public vnfList: string[] = [];
63 /** Contains node type @public */
64 public nodeTypeRef: string;
65 /** Contains VNFD Information @public */
66 public vnfdInfo: VNFDNODE = { shortName: '', description: '', version: '', id: '', name: '' };
67 /** Contains right panel box information @public */
68 public showRightSideInfo: string = '';
69 /** Add the fixed class for the freeze @public */
70 public fixedClass: string = 'fixed';
71 /** Check the loading results @public */
72 public isLoadingResults: boolean = true;
73 /** Give the message for the loading @public */
74 public message: string = 'PLEASEWAIT';
75 /** Assign the forcesimulation active @public */
76 public forceSimulationActive: boolean = false;
77 /** Assign pinned class for the button when freezed @public */
78 public classApplied: boolean = false;
79 /** Contains sidebar open status @public */
80 public sideBarOpened: boolean = false;
82 /** Contains SVG attributes @private */
83 // tslint:disable-next-line:no-any
85 /** Contains forced node animations @private */
86 // tslint:disable-next-line:no-any
88 /** Contains the Drag line */
89 // tslint:disable-next-line: no-any
90 private dragLine: any;
91 /** Contains id of the node @private */
92 private identifier: string;
93 /** Contains path information of the node */
94 // tslint:disable-next-line:no-any
96 /** Contains node network @private */
97 // tslint:disable-next-line:no-any
99 /** Contains node network @private */
100 // tslint:disable-next-line:no-any
101 private virutualDeploymentUnit: any;
102 /** Contains node connectionPoint @private */
103 // tslint:disable-next-line:no-any
104 private connectionPoint: any;
105 /** Contains node intConnectionPoint @private */
106 // tslint:disable-next-line:no-any
107 private intConnectionPoint: any;
108 /** Contains the node information @private */
109 private nodes: VNFDNODE[] = [];
110 /** Contains the link information of nodes @private */
111 private links: {}[] = [];
112 /** Instance of the rest service @private */
113 private restService: RestService;
114 /** Service holds the router information @private */
115 private router: Router;
116 /** Service contails all the shared service information @private */
117 private sharedService: SharedService;
118 /** Holds teh instance of AuthService class of type AuthService @private */
119 private activatedRoute: ActivatedRoute;
120 /** Notifier service to popup notification @private */
121 private notifierService: NotifierService;
122 /** Controls the header form @private */
123 private headers: HttpHeaders;
124 /** Contains tranlsate instance @private */
125 private translateService: TranslateService;
126 /** Rendered nodes represent network @private */
127 // tslint:disable-next-line:no-any
128 private gNetwork: any;
129 /** Rendered nodes represent VDU @private */
130 // tslint:disable-next-line:no-any
131 private gVirutualDeploymentUnit: any;
132 /** Rendered nodes represent connection point @private */
133 // tslint:disable-next-line:no-any
134 private gConnectionPoint: any;
135 /** Rendered nodes represent internal connection point @private */
136 // tslint:disable-next-line:no-any
137 private gIntConnectionPoint: any;
138 /** Contains all the information about VNF Details @private */
139 private vnfdPackageDetails: VNFDNODE;
140 /** Conatins mousedown action @private */
141 private mousedownNode: COMPOSERNODES = null;
142 /** Conatins mouseup action @private */
143 private mouseupNode: COMPOSERNODES = null;
144 /** Conatins current Selection node action @private */
145 private currentSelectedNode: COMPOSERNODES = null;
146 /** Add the activeNode for the selected @private */
147 private activeNode: string = 'active';
148 /** Contains lastkeypressed instance @private */
149 private lastKeyDown: number = -1;
150 /** Contains VDU Information @private */
151 private vduInfo: VDU;
152 /** Contains Internal VL Information @private */
153 private intvlInfo: InternalVLD;
154 /** Contains Connection Point Information @private */
155 private cpInfo: CONNECTIONPOINT;
156 /** Contains Internal Connection Point Information @private */
157 private intcpInfo: VLDInternalConnectionPoint;
158 /** Instance of the modal service @private */
159 private modalService: NgbModal;
161 constructor(injector: Injector) {
162 this.injector = injector;
163 this.restService = this.injector.get(RestService);
164 this.dataService = this.injector.get(DataService);
165 this.router = this.injector.get(Router);
166 this.activatedRoute = this.injector.get(ActivatedRoute);
167 this.notifierService = this.injector.get(NotifierService);
168 this.translateService = this.injector.get(TranslateService);
169 this.sharedService = this.injector.get(SharedService);
170 this.modalService = this.injector.get(NgbModal);
173 * Lifecyle Hooks the trigger before component is instantiate
175 public ngOnInit(): void {
176 // tslint:disable-next-line:no-backbone-get-set-outside-model
177 this.identifier = this.activatedRoute.snapshot.paramMap.get('id');
179 this.headers = new HttpHeaders({
180 'Content-Type': 'application/zip',
181 Accept: 'application/json',
182 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
186 /** Prepare information for node creation of VNFD @public */
187 public generateData(): void {
188 this.nodes = []; this.links = []; this.vnfdPackageDetails = null;
189 this.showRightSideInfo = 'vnfdInfo';
190 const httpOptions: GETAPIURLHEADER = {
191 headers: new HttpHeaders({
192 'Content-Type': 'application/zip',
193 Accept: 'text/plain',
194 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
198 this.restService.getResource(environment.VNFPACKAGES_URL + '/' + this.identifier + '/vnfd', httpOptions)
199 .subscribe((vnfdPackageDetails: VNFDNODE) => {
201 const getJson: string = jsyaml.load(vnfdPackageDetails.toString(), { json: true });
202 if (getJson.hasOwnProperty('vnfd-catalog')) {
203 this.vnfdPackageDetails = getJson['vnfd-catalog'].vnfd[0];
204 } else if (getJson.hasOwnProperty('vnfd:vnfd-catalog')) {
205 this.vnfdPackageDetails = getJson['vnfd:vnfd-catalog'].vnfd[0];
206 } else if (getJson.hasOwnProperty('vnfd')) {
207 // tslint:disable-next-line: no-string-literal
208 this.vnfdPackageDetails = getJson['vnfd'][0];
210 this.generateCPPoint(this.vnfdPackageDetails);
211 this.generateVDU(this.vnfdPackageDetails);
212 this.generateInternalVLD(this.vnfdPackageDetails);
213 this.generateInternalCP(this.vnfdPackageDetails);
214 this.generateIntVLCPLinks(this.vnfdPackageDetails);
215 this.generateVDUCPLinks(this.vnfdPackageDetails);
216 this.createNode(this.nodes);
217 this.vnfdInfo.shortName = this.vnfdPackageDetails['short-name'];
218 this.vnfdInfo.description = this.vnfdPackageDetails.description;
219 this.vnfdInfo.version = this.vnfdPackageDetails.version;
220 this.vnfdInfo.id = this.vnfdPackageDetails.id;
221 this.vnfdInfo.name = this.vnfdPackageDetails.name;
223 this.notifierService.notify('error', this.translateService.instant('ERROR'));
225 this.isLoadingResults = false;
226 }, (error: ERRORDATA) => {
227 error.error = typeof error.error === 'string' ? jsyaml.load(error.error) : error.error;
228 if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
229 this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
231 this.restService.handleError(error, 'get');
233 this.isLoadingResults = false;
234 this.showRightSideInfo = '';
237 /** Events handles at drag on D3 region @public */
238 // tslint:disable-next-line:no-any
239 public drag(ev: any): void {
240 ev.dataTransfer.setData('text', ev.target.id);
242 /** Events handles drop at D3 region @public */
243 public drop(ev: DragEvent): void {
245 this.nodeTypeRef = ev.dataTransfer.getData('text');
246 if (this.nodeTypeRef === 'vdu') {
247 this.svg.selectAll('*').remove();
248 this.vduDropCompose();
249 } else if (this.nodeTypeRef === 'cp') {
250 this.svg.selectAll('*').remove();
251 this.cpDropCompose();
252 } else if (this.nodeTypeRef === 'intvl') {
253 this.svg.selectAll('*').remove();
254 this.intvlDropCompose();
257 /** Events handles allow drop on D3 region @public */
258 public allowDrop(ev: DragEvent): void {
261 /** Generate and list CP points @public */
262 public generateCPPoint(vnfdPackageDetails: VNFDNODE): void {
263 if (vnfdPackageDetails['connection-point'] !== undefined) {
264 vnfdPackageDetails['connection-point'].forEach((cp: CONNECTIONPOINT) => {
265 this.nodes.push({ id: cp.name, nodeTypeRef: 'cp', name: cp.name, type: cp.type });
269 /** Generate and list VDU @public */
270 public generateVDU(vnfdPackageDetails: VNFDNODE): void {
271 if (vnfdPackageDetails.vdu !== undefined) {
272 vnfdPackageDetails.vdu.forEach((vdu: VDU) => {
274 id: vdu.name, nodeTypeRef: 'vdu', 'cloud-init-file': vdu['cloud-init-file'], count: vdu.count, description: vdu.description,
275 vduID: vdu.id, name: vdu.name, interface: vdu.interface, 'vm-flavor': vdu['vm-flavor']
280 /** Generate and list Internal VLD @public */
281 public generateInternalVLD(vnfdPackageDetails: VNFDNODE): void {
282 if (vnfdPackageDetails['internal-vld'] !== undefined) {
283 vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => {
285 id: internalVLD.name, nodeTypeRef: 'intvl', intVLID: internalVLD.id,
286 'internal-connection-point': internalVLD['internal-connection-point'],
287 'ip-profile-ref': internalVLD['ip-profile-ref'], name: internalVLD.name, 'short-name': internalVLD['short-name'],
288 type: internalVLD.type
293 /** Generate and list Internal CP @public */
294 public generateInternalCP(vnfdPackageDetails: VNFDNODE): void {
295 if (vnfdPackageDetails.vdu !== undefined) {
296 vnfdPackageDetails.vdu.forEach((intCP: VDUInternalConnectionPoint) => {
297 if (intCP['internal-connection-point'] !== undefined) {
298 intCP['internal-connection-point'].forEach((internalCP: VDUInternalConnectionPoint) => {
300 id: internalCP.name, nodeTypeRef: 'intcp', name: internalCP.name,
301 'short-name': internalCP['short-name'], type: internalCP.type
308 /** Generate VDU External and Internal CP Links @public */
309 public generateVDUCPLinks(vnfdPackageDetails: VNFDNODE): void {
310 if (vnfdPackageDetails.vdu !== undefined) {
311 vnfdPackageDetails.vdu.forEach((vdu: VDU) => {
312 const vduLink: string = vdu.name;
313 if (vdu.interface !== undefined) {
314 vdu.interface.forEach((interfaceDetails: VNFDInterface) => {
315 if (interfaceDetails['external-connection-point-ref'] !== undefined) {
316 this.links.push({ source: vduLink, target: interfaceDetails['external-connection-point-ref'] });
318 if (interfaceDetails['internal-connection-point-ref'] !== undefined) {
319 this.links.push({ source: vduLink, target: interfaceDetails['internal-connection-point-ref'] });
326 /** Generate Network/VLD/Internal VirtualLink, CP Links @public */
327 public generateIntVLCPLinks(vnfdPackageDetails: VNFDNODE): void {
328 if (vnfdPackageDetails['internal-vld'] !== undefined) {
329 vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => {
330 const vldName: string = internalVLD.name;
331 if (internalVLD['internal-connection-point'] !== undefined) {
332 internalVLD['internal-connection-point'].forEach((intCP: VLDInternalConnectionPoint) => {
333 this.links.push({ source: vldName, target: intCP['id-ref'] });
339 /** VNFD details can be saved on users inputs @public */
340 public saveVNFD(): void {
341 this.vnfdPackageDetails['short-name'] = this.vnfdInfo.shortName;
342 this.vnfdPackageDetails.description = this.vnfdInfo.description;
343 this.vnfdPackageDetails.version = this.vnfdInfo.version;
344 this.vnfdPackageDetails.id = this.vnfdInfo.id;
345 this.vnfdPackageDetails.name = this.vnfdInfo.name;
346 this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
347 delete this.vnfdPackageDetails.shortName;
349 /** VDU details can be saved on users inputs @public */
350 public saveVDU(vduID: string): void {
351 this.vnfdPackageDetails.vdu.forEach((ref: VDU) => {
352 if (ref.id === vduID) {
353 ref.count = this.vduInfo.count;
354 ref.description = this.vduInfo.description;
355 ref.image = this.vduInfo.image;
356 ref.id = this.vduInfo.id;
357 ref.name = this.vduInfo.name;
360 this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
362 /** IntVL details can be saved on users inputs @public */
363 public saveIntVL(intVLID: string): void {
364 this.vnfdPackageDetails['internal-vld'].forEach((ref: InternalVLD) => {
365 if (ref.id === intVLID) {
366 ref['short-name'] = this.intvlInfo.shortName;
367 ref.name = this.intvlInfo.name;
368 ref.type = this.intvlInfo.type;
369 ref['ip-profile-ref'] = !isNullOrUndefined(this.intvlInfo.ipProfileRef) ? this.intvlInfo.ipProfileRef : '';
370 ref.id = this.intvlInfo.id;
371 delete ref.shortName;
372 delete ref.ipProfileRef;
375 this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
377 /** IntVL details can be saved on users inputs @public */
378 public saveCP(cpName: string): void {
379 this.vnfdPackageDetails['connection-point'].forEach((ref: CONNECTIONPOINT) => {
380 if (ref.name === cpName) {
381 if (!isNullOrUndefined(this.cpInfo.type)) {
382 ref.type = this.cpInfo.type;
384 ref.name = this.cpInfo.name;
387 this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
389 /** Edit topology @public */
390 public onEdit(): void {
391 this.router.navigate(['/packages/vnf/edit/', this.identifier]).catch();
393 /** Show Info @public */
394 public showInfo(): void {
395 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
396 modalRef.componentInstance.topologyType = 'Info';
397 modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.INFO');
398 modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
404 /** Event to freeze the animation @public */
405 public onFreeze(): void {
406 this.classApplied = !this.classApplied;
407 const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass);
408 d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive);
409 if (alreadyFixedIsActive) {
412 this.forceSimulationActive = alreadyFixedIsActive;
413 this.nodes.forEach((d: COMPOSERNODES) => {
414 d.fx = (alreadyFixedIsActive) ? null : d.x;
415 d.fy = (alreadyFixedIsActive) ? null : d.y;
417 if (alreadyFixedIsActive) {
418 this.force.restart();
421 /** Events handles when dragended @public */
422 public toggleSidebar(): void {
423 this.sideBarOpened = !this.sideBarOpened;
424 this.deselectAllNodes();
425 this.showRightSideInfo = 'vnfdInfo';
427 /** Get the default Configuration of containers @private */
428 private getGraphContainerAttr(): GRAPHDETAILS {
441 sourcePaddingYes: 17,
451 /** Node is created and render at D3 region @private */
452 private createNode(nodes: VNFDNODE[]): void {
453 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
454 d3.selectAll('svg#graphContainer > *').remove();
455 d3.select(window).on('keydown', () => { this.keyDown(); });
456 d3.select(window).on('keyup', () => { this.keyUp(); });
457 this.svg = d3.select('#graphContainer').attr('oncontextmenu', 'return false;').attr('width', graphContainerAttr.width)
458 .attr('height', graphContainerAttr.height).on('mousemove', () => { this.mousemove(); });
459 this.force = d3.forceSimulation()
460 .force('link', d3.forceLink().id((d: TickPath) => d.id).distance(graphContainerAttr.distance))
461 .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength))
462 .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex))
463 .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey))
464 .on('tick', () => { this.tick(); });
465 this.path = this.svg.append('svg:g').selectAll('path');
466 this.dragLine = this.svg.append('svg:path').attr('class', 'link dragline hidden').attr('d', 'M0,0L0,0');
467 this.network = this.svg.append('svg:g').selectAll('network');
468 this.virutualDeploymentUnit = this.svg.append('svg:g').selectAll('virutualDeploymentUnit');
469 this.connectionPoint = this.svg.append('svg:g').selectAll('connectionPoint');
470 this.intConnectionPoint = this.svg.append('svg:g').selectAll('intConnectionPoint');
473 /** Update force layout (called automatically each iteration) @private */
474 private tick(): void {
475 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
476 this.path.attr('d', (d: Tick) => {
477 const deltaX: number = d.target.x - d.source.x; const deltaY: number = d.target.y - d.source.y;
478 const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
479 const normX: number = deltaX / dist; const normY: number = deltaY / dist;
480 const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
481 const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
482 const sourceX: number = d.source.x + (sourcePadding * normX); const sourceY: number = d.source.y + (sourcePadding * normY);
483 const targetX: number = d.target.x - (targetPadding * normX); const targetY: number = d.target.y - (targetPadding * normY);
484 return `M${sourceX},${sourceY}L${targetX},${targetY}`;
485 }).on('dblclick', (d: Tick) => { this.getDeleteLinkConfirmation(d); });
486 this.network.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`);
487 this.virutualDeploymentUnit.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`);
488 this.connectionPoint.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`);
489 this.intConnectionPoint.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`);
491 /** Update graph (called when needed) @private */
492 private restart(nodes: VNFDNODE[]): void {
493 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
494 this.path = this.path.data(this.links);
495 const cpNodes: {}[] = []; const vduNodes: {}[] = []; const vlNodes: {}[] = []; const intcpNodes: {}[] = []; //Nodes are known by id
496 nodes.forEach((nodeList: VNFDNODE) => {
497 if (nodeList.nodeTypeRef === 'cp') { cpNodes.push(nodeList); }
498 else if (nodeList.nodeTypeRef === 'vdu') { vduNodes.push(nodeList); }
499 else if (nodeList.nodeTypeRef === 'intvl') { vlNodes.push(nodeList); }
500 else if (nodeList.nodeTypeRef === 'intcp') { intcpNodes.push(nodeList); }
502 this.network = this.network.data(vlNodes, (d: { id: number }) => d.id);
503 this.virutualDeploymentUnit = this.virutualDeploymentUnit.data(vduNodes, (d: { id: number }) => d.id);
504 this.connectionPoint = this.connectionPoint.data(cpNodes, (d: { id: number }) => d.id);
505 this.intConnectionPoint = this.intConnectionPoint.data(intcpNodes, (d: { id: number }) => d.id);
506 this.resetAndCreateNodes();
507 this.force.nodes(nodes).force('link').links(this.links); //Set the graph in motion
508 this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
510 /** Rest and create nodes @private */
511 private resetAndCreateNodes(): void {
512 this.path.exit().remove();
513 this.network.exit().remove();
514 this.virutualDeploymentUnit.exit().remove();
515 this.connectionPoint.exit().remove();
516 this.intConnectionPoint.exit().remove();
519 this.getgVirutualDeploymentUnit();
520 this.getgConnectionPoint();
521 this.getgIntConnectionPoint();
522 this.network = this.gNetwork.merge(this.network);
523 this.virutualDeploymentUnit = this.gVirutualDeploymentUnit.merge(this.virutualDeploymentUnit);
524 this.connectionPoint = this.gConnectionPoint.merge(this.connectionPoint);
525 this.intConnectionPoint = this.gIntConnectionPoint.merge(this.intConnectionPoint);
526 this.path.merge(this.path);
528 /** Setting the Path @private */
529 private getPathNodes(): void {
530 this.path = this.path.enter().append('svg:path').attr('class', 'link');
532 /** Settings all the network attributes of nodes @private */
533 private getgNetwork(): void {
534 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
535 this.gNetwork = this.network.enter().append('svg:g');
536 this.gNetwork.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
537 this.gNetwork.append('svg:image')
539 .attr('x', graphContainerAttr.imageX)
540 .attr('y', graphContainerAttr.imageY)
541 .attr('id', (d: VNFDNODE) => { return d.id; })
542 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
543 .attr('xlink:href', 'assets/images/INTVL.svg')
544 .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); })
545 .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); })
546 .on('click', (d: VNFDNODE) => { this.singleClick(this.network, d); this.onNodeClickToggleSidebar(); })
547 .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); });
548 this.gNetwork.append('svg:text')
549 .attr('class', 'node_text')
550 .attr('y', graphContainerAttr.textY)
551 .text((d: { id: number }) => d.id);
553 /** Settings all the connection point attributes of nodes @private */
554 private getgVirutualDeploymentUnit(): void {
555 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
556 this.gVirutualDeploymentUnit = this.virutualDeploymentUnit.enter().append('svg:g');
557 this.gVirutualDeploymentUnit.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
558 this.gVirutualDeploymentUnit.append('svg:image')
560 .attr('x', graphContainerAttr.imageX)
561 .attr('y', graphContainerAttr.imageY)
562 .attr('id', (d: VNFDNODE) => { return d.id; })
563 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
564 .attr('xlink:href', 'assets/images/VDU.svg')
565 .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); })
566 .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); })
567 .on('click', (d: VNFDNODE) => { this.singleClick(this.virutualDeploymentUnit, d); this.onNodeClickToggleSidebar(); })
568 .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); });
569 this.gVirutualDeploymentUnit.append('svg:text')
570 .attr('class', 'node_text')
571 .attr('y', graphContainerAttr.textY)
572 .text((d: { id: string }) => d.id);
574 /** Settings all the connection point attributes of nodes @private */
575 private getgConnectionPoint(): void {
576 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
577 this.gConnectionPoint = this.connectionPoint.enter().append('svg:g');
578 this.gVirutualDeploymentUnit.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
579 this.gConnectionPoint.append('svg:image')
581 .attr('x', graphContainerAttr.imageX)
582 .attr('y', graphContainerAttr.imageY)
583 .attr('id', (d: VNFDNODE) => { return d.id; })
584 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
585 .attr('xlink:href', 'assets/images/CP-VNF.svg')
586 .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); })
587 .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); })
588 .on('click', (d: VNFDNODE) => { this.singleClick(this.connectionPoint, d); this.onNodeClickToggleSidebar(); })
589 .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); });
590 this.gConnectionPoint.append('svg:text')
591 .attr('class', 'node_text')
592 .attr('y', graphContainerAttr.textY)
593 .text((d: { id: string }) => d.id);
595 /** Settings all the internal connection point attributes of nodes @private */
596 private getgIntConnectionPoint(): void {
597 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
598 this.gIntConnectionPoint = this.intConnectionPoint.enter().append('svg:g');
599 this.gIntConnectionPoint.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
600 this.gIntConnectionPoint.append('svg:image')
602 .attr('x', graphContainerAttr.imageX)
603 .attr('y', graphContainerAttr.imageY)
604 .attr('id', (d: VNFDNODE) => { return d.id; })
605 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
606 .attr('xlink:href', 'assets/images/INTCP.svg')
607 .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); })
608 .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); })
609 .on('click', (d: VNFDNODE) => { this.singleClick(this.intConnectionPoint, d); this.onNodeClickToggleSidebar(); })
610 .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); });
611 this.gIntConnectionPoint.append('svg:text')
612 .attr('class', 'node_text')
613 .attr('y', graphContainerAttr.textY)
614 .text((d: { id: string }) => d.id);
616 /** Drop VDU Composer Data @private */
617 private vduDropCompose(): void {
618 const randomID: string = this.sharedService.randomString();
619 const vduNode: VNFDNODE[] = [{
620 nodeTypeRef: 'vdu', id: 'vdu_' + randomID, count: 1, description: '', name: 'vdu_' + randomID, image: 'ubuntu',
621 interface: [], 'internal-connection-point': [], 'monitoring-param': [], 'vm-flavor': {}
623 const nodeCopy: VNFDNODE[] = this.nodes;
624 Array.prototype.push.apply(vduNode, nodeCopy);
625 this.nodes = vduNode;
626 if (this.vnfdPackageDetails.vdu === undefined) { this.vnfdPackageDetails.vdu = []; }
627 this.vnfdPackageDetails.vdu.push({
628 id: 'vdu_' + randomID, count: 1, description: '', name: 'vdu_' + randomID, image: 'ubuntu',
629 interface: [], 'internal-connection-point': [], 'monitoring-param': [], 'vm-flavor': {}
631 this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
633 /** Drop CP Composer Data @private */
634 private cpDropCompose(): void {
635 const randomID: string = this.sharedService.randomString();
636 const cpNode: VNFDNODE[] = [{ nodeTypeRef: 'cp', id: 'cp_' + randomID, name: 'cp_' + randomID }];
637 const nodeCopy: VNFDNODE[] = this.nodes;
638 Array.prototype.push.apply(cpNode, nodeCopy);
640 if (this.vnfdPackageDetails['connection-point'] === undefined) {
641 this.vnfdPackageDetails['connection-point'] = [];
643 this.vnfdPackageDetails['connection-point'].push({
644 id: 'cp_' + randomID, name: 'cp_' + randomID, type: 'VPORT'
646 this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
648 /** Drop IntVL Composer Data @private */
649 private intvlDropCompose(): void {
650 const randomID: string = this.sharedService.randomString();
651 const intvlNode: VNFDNODE[] = [{
652 nodeTypeRef: 'intvl', id: 'vnf_vl_' + randomID, name: 'vnf_vl_' + randomID, 'short-name': 'vnf_vl_' + randomID, 'ip-profile-ref': '',
655 const nodeCopy: VNFDNODE[] = this.nodes;
656 Array.prototype.push.apply(intvlNode, nodeCopy);
657 this.nodes = intvlNode;
658 if (this.vnfdPackageDetails['internal-vld'] === undefined) { this.vnfdPackageDetails['internal-vld'] = []; }
659 this.vnfdPackageDetails['internal-vld'].push({
660 id: 'vnf_vl_' + randomID, name: 'vnf_vl_' + randomID, 'short-name': 'vnf_vl_' + randomID,
661 'ip-profile-ref': '', type: 'ELAN', 'internal-connection-point': []
663 this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
665 /** Add the Add Nodes Data @private */
666 private addNodes(apiURL: string, identifier: string, data: VNFDNODE): void {
667 this.isLoadingResults = true;
668 const apiURLHeader: APIURLHEADER = {
669 url: apiURL + '/' + identifier + '/package_content',
670 httpOptions: { headers: this.headers }
672 const vnfData: {} = {};
673 vnfData['vnfd:vnfd-catalog'] = {};
674 vnfData['vnfd:vnfd-catalog'].vnfd = [];
675 vnfData['vnfd:vnfd-catalog'].vnfd.push(data);
676 const descriptorInfo: string = jsyaml.dump(vnfData, { sortKeys: true });
677 this.sharedService.targzFile({ packageType: 'vnfd', id: this.identifier, descriptor: descriptorInfo })
678 .then((content: ArrayBuffer): void => {
679 this.restService.putResource(apiURLHeader, content).subscribe((res: {}) => {
681 this.notifierService.notify('success', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.UPDATEDSUCCESSFULLY'));
682 this.isLoadingResults = false;
683 }, (error: ERRORDATA) => {
685 this.restService.handleError(error, 'put');
686 this.isLoadingResults = false;
688 }).catch((): void => {
689 this.notifierService.notify('error', this.translateService.instant('ERROR'));
690 this.isLoadingResults = false;
693 /** Events handles when mousedown click it will capture the selected node data @private */
694 private mouseDown(d: VNFDNODE): void {
695 event.preventDefault();
696 if (d3.event.ctrlKey) { return; }
697 if (d3.event.shiftKey) {
698 this.mousedownNode = d;
699 this.currentSelectedNode = (this.mousedownNode === this.currentSelectedNode) ? null : this.mousedownNode;
700 this.dragLine.classed('hidden', false)
701 .attr('d', `M${this.mousedownNode.x},${this.mousedownNode.y}L${this.mousedownNode.x},${this.mousedownNode.y}`);
704 /** Event handles when mouseup event occures @private */
705 private mouseUp(d: VNFDNODE): void {
706 if (!this.mousedownNode) { return; }
707 this.dragLine.classed('hidden', true);
708 this.mouseupNode = d;
709 if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'intcp') {
710 this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKVDUANDINTCP'));
713 else if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'vdu') {
714 this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKVDUANDVDU'));
717 else if (this.mousedownNode.nodeTypeRef === 'intcp' && this.mouseupNode.nodeTypeRef === 'vdu') {
718 this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKINTCPANDVDU'));
721 else if (this.mousedownNode.nodeTypeRef === 'cp' && this.mouseupNode.nodeTypeRef === 'intvl') {
722 this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKCPANDVNFVL'));
725 else if (this.mousedownNode.nodeTypeRef === 'intvl' && this.mouseupNode.nodeTypeRef === 'cp') {
726 this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKVNFVLANDCP'));
729 else if (this.mousedownNode.nodeTypeRef === 'intcp' && this.mouseupNode.nodeTypeRef === 'cp') {
730 this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKINTCPANDCP'));
733 else if (this.mousedownNode.nodeTypeRef === 'cp' && this.mouseupNode.nodeTypeRef === 'intcp') {
734 this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKCPANDINTCP'));
736 } else if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'cp') {
737 this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => {
738 if (vduDetails.id === this.mousedownNode.id) {
739 if (vduDetails.interface === undefined) { vduDetails.interface = []; }
740 vduDetails.interface.push({
741 'external-connection-point-ref': this.mouseupNode.id, 'mgmt-interface': true,
742 name: 'eth_' + this.sharedService.randomString(),
743 'virtual-interface': { type: 'VIRTIO' },
746 if (vduDetails['internal-connection-point'] === undefined) {
747 vduDetails['internal-connection-point'] = [];
749 if (vduDetails['monitoring-param'] === undefined) {
750 vduDetails['monitoring-param'] = [];
752 if (vduDetails['vm-flavor'] === undefined) {
753 vduDetails['vm-flavor'] = {};
757 this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
759 } else if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'intvl') {
760 const setIntCP: string = 'intcp_' + this.sharedService.randomString();
761 this.vnfdPackageDetails['internal-vld'].forEach((vldInternal: InternalVLD) => {
762 if (vldInternal.id === this.mouseupNode.id) {
763 if (vldInternal['internal-connection-point'] === undefined) { vldInternal['internal-connection-point'] = []; }
764 vldInternal['internal-connection-point'].push({ 'id-ref': setIntCP });
767 this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => {
768 if (vduDetails.id === this.mousedownNode.id) {
769 if (vduDetails.interface === undefined) {
770 vduDetails.interface = [];
772 vduDetails.interface.push({
773 'internal-connection-point-ref': setIntCP, name: 'int_' + setIntCP, type: 'INTERNAL', 'virtual-interface': { type: 'VIRTIO' }
775 if (vduDetails['internal-connection-point'] === undefined) {
776 vduDetails['internal-connection-point'] = [];
778 vduDetails['internal-connection-point'].push({
779 id: setIntCP, name: setIntCP, 'short-name': setIntCP, type: 'VPORT'
783 this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
787 this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.INVALIDSELECTION'));
790 this.resetMouseActions();
791 this.currentSelectedNode = null;
793 /** Events handles when mousemove it will capture the selected node data @private */
794 private mousemove(): void {
795 if (!this.mousedownNode) { return; }
796 this.dragLine.attr('d',
797 `M${this.mousedownNode.x},${this.mousedownNode.y}L${d3.mouse(d3.event.currentTarget)[0]},${d3.mouse(d3.event.currentTarget)[1]}`);
799 /** reset Mouse varaibles @private */
800 private resetMouseActions(): void {
801 this.mousedownNode = null;
802 this.mouseupNode = null;
804 /** Key press event @private */
805 private keyDown(): void {
806 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
807 if (this.lastKeyDown !== -1) { return; }
808 this.lastKeyDown = d3.event.keyCode;
809 if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
810 this.gNetwork.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended));
811 this.gVirutualDeploymentUnit.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended));
812 this.gConnectionPoint.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended));
813 this.gIntConnectionPoint.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended));
814 this.svg.classed('ctrl', true);
817 /** Key realse event @private */
818 private keyUp(): void {
819 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
820 this.lastKeyDown = -1;
821 if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
822 this.gNetwork.on('.drag', null);
823 this.gVirutualDeploymentUnit.on('.drag', null);
824 this.gConnectionPoint.on('.drag', null);
825 this.gIntConnectionPoint.on('.drag', null);
826 this.svg.classed('ctrl', false);
829 /** Mosue Drag Line false if it is not satisfied @private */
830 private deselectPath(): void {
831 this.dragLine.classed('hidden', true).attr('d', 'M0,0L0,0');
833 /** Events handles when Shift Click to perform create cp @private */
834 // tslint:disable-next-line: no-any
835 private singleClick(nodeSelected: any, d: VNFDNODE): void {
836 this.selectedNode(nodeSelected, d);
838 /** Get confirmation Before Deleting the Node in Topology @private */
839 private getDeleteNodeConfirmation(d: VNFDNODE): void {
840 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
841 modalRef.componentInstance.topologyType = 'Delete';
842 modalRef.componentInstance.topologyname = d.name;
843 if (d.nodeTypeRef === 'vdu') {
844 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VDU';
845 } else if (d.nodeTypeRef === 'intvl') {
846 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.INTVL';
847 } else if (d.nodeTypeRef === 'cp') {
848 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.CONNECTIONPOINT';
849 } else if (d.nodeTypeRef === 'intcp') {
850 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.INTCP';
852 modalRef.result.then((result: MODALCLOSERESPONSEDATA) => {
858 /** Delete nodes @private */
859 private deleteNode(d: VNFDNODE): void {
860 const deletedNode: VNFDNODE = d;
861 this.nodes.forEach((node: VNFDNODE) => {
862 if (node.id === d.id) {
863 if (deletedNode.nodeTypeRef === 'cp') {
864 if (this.vnfdPackageDetails.vdu !== undefined) {
865 this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => {
866 if (vduDetails.interface !== undefined) {
867 const interfacePos: number = vduDetails.interface.map((e: VNFDInterface) => { return e['external-connection-point-ref']; })
869 if (interfacePos >= 0) {
870 vduDetails.interface.splice(interfacePos, 1);
875 const cpPos: number = this.vnfdPackageDetails['connection-point'].map((e: CONNECTIONPOINT) => { return e.name; })
878 this.vnfdPackageDetails['connection-point'].splice(cpPos, 1);
880 } else if (deletedNode.nodeTypeRef === 'intcp') {
881 this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => {
883 const interfacePos: number = vduDetails.interface.map((e: VNFDInterface) => { return e['internal-connection-point-ref']; })
885 if (interfacePos >= 0) {
886 vduDetails.interface.splice(interfacePos, 1);
888 // Delete Internal CP
889 const interCPPos: number = vduDetails['internal-connection-point']
890 .map((e: VDUInternalConnectionPoint) => { return e.id; })
892 if (interCPPos >= 0) {
893 vduDetails['internal-connection-point'].splice(interCPPos, 1);
896 if (this.vnfdPackageDetails['internal-vld'] !== undefined && this.vnfdPackageDetails['internal-vld'].length > 0) {
897 this.vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => {
898 const interfacePos: number = internalVLD['internal-connection-point']
899 .map((e: VLDInternalConnectionPoint) => { return e['id-ref']; }).indexOf(d.id);
900 if (interfacePos >= 0) {
901 internalVLD['internal-connection-point'].splice(interfacePos, 1);
905 } else if (deletedNode.nodeTypeRef === 'intvl') {
906 const intvlPos: number = this.vnfdPackageDetails['internal-vld']
907 .map((e: InternalVLD) => { return e.name; }).indexOf(d.id);
909 this.vnfdPackageDetails['internal-vld'].splice(intvlPos, 1);
911 } else if (deletedNode.nodeTypeRef === 'vdu') {
912 const internalCPList: string[] = [];
913 if (deletedNode.interface !== undefined && deletedNode.interface.length > 0) {
914 deletedNode.interface.forEach((interfaceNode: InternalVLD) => {
915 if (interfaceNode['internal-connection-point-ref'] !== undefined) {
916 internalCPList.push(interfaceNode['internal-connection-point-ref']);
920 internalCPList.forEach((list: string) => {
921 if (this.vnfdPackageDetails['internal-vld'] !== undefined && this.vnfdPackageDetails['internal-vld'].length > 0) {
922 this.vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => {
923 const interfacePos: number = internalVLD['internal-connection-point']
924 .map((e: VLDInternalConnectionPoint) => { return e['id-ref']; }).indexOf(list);
925 if (interfacePos >= 0) {
926 internalVLD['internal-connection-point'].splice(interfacePos, 1);
931 const vduPos: number = this.vnfdPackageDetails.vdu.map((e: VDU) => { return e.id; }).indexOf(d.id);
933 this.vnfdPackageDetails.vdu.splice(vduPos, 1);
936 this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.INVALIDSELECTION'));
938 this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails);
942 /** Get confirmation before deleting the ink in Topology @private */
943 private getDeleteLinkConfirmation(d: Tick): void {
944 this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.YOUCANNOTDELETELINK'));
946 /** Selected nodes @private */
947 // tslint:disable-next-line: no-any
948 private selectedNode(nodeSeleced: any, d: VNFDNODE): void {
949 const alreadyIsActive: boolean = nodeSeleced.select('#' + d.id).classed(this.activeNode);
950 this.deselectAllNodes();
951 nodeSeleced.select('#' + d.id).classed(this.activeNode, !alreadyIsActive);
952 if (d.nodeTypeRef === 'vdu' && !alreadyIsActive) {
953 this.vnfdPackageDetails.vdu.forEach((res: VDU) => {
954 if (res.name === d.id) {
955 this.showRightSideInfo = 'vduInfo';
959 } else if (d.nodeTypeRef === 'intvl' && !alreadyIsActive) {
960 this.vnfdPackageDetails['internal-vld'].forEach((res: InternalVLD) => {
961 if (res.name === d.id) {
962 this.showRightSideInfo = 'intvlInfo';
963 this.intvlInfo = res;
964 this.intvlInfo.shortName = res['short-name'];
965 this.intvlInfo.ipProfileRef = res['ip-profile-ref'];
968 } else if (d.nodeTypeRef === 'cp' && !alreadyIsActive) {
969 this.vnfdPackageDetails['connection-point'].forEach((res: CONNECTIONPOINT) => {
970 if (res.name === d.id) {
971 this.showRightSideInfo = 'cpInfo';
976 else if (d.nodeTypeRef === 'intcp' && !alreadyIsActive) {
978 this.showRightSideInfo = 'intcpInfo';
979 this.intcpInfo.shortName = d['short-name'];
981 this.showRightSideInfo = 'vnfdInfo';
984 /** De-select all the selected nodes @private */
985 private deselectAllNodes(): void {
986 this.network.select('image').classed(this.activeNode, false);
987 this.virutualDeploymentUnit.select('image').classed(this.activeNode, false);
988 this.connectionPoint.select('image').classed(this.activeNode, false);
989 this.intConnectionPoint.select('image').classed(this.activeNode, false);
991 /** Events handles when dragstarted @private */
992 private dragstarted(d: COMPOSERNODES): void {
996 /** Events handles when dragged @private */
997 private dragged(d: COMPOSERNODES): void {
998 d.fx = d.x = d3.event.x;
999 d.fy = d.y = d3.event.y;
1001 /** Events handles when dragended @private */
1002 private dragended(d: COMPOSERNODES): void {
1003 if (this.forceSimulationActive) {
1009 this.forceSimulationActive = false;
1012 /** Events handles when node double click @private */
1013 private onNodedblClickToggleSidebar(): void {
1014 this.sideBarOpened = false;
1016 /** Events handles when node single click @private */
1017 private onNodeClickToggleSidebar(): void {
1018 this.sideBarOpened = true;