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 NS Compose Component
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, CONSTANTNUMBER, ERRORDATA, MODALCLOSERESPONSEDATA, MODALCLOSERESPONSEWITHCP } 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';
42 Tick, TickPath, VLC, VLD, VNFPROFILE
44 import { RestService } from 'RestService';
45 import { SharedService } from 'SharedService';
46 import { isNullOrUndefined } from 'util';
47 import { VNFD, VNFData } from 'VNFDModel';
51 * @Component takes NSComposerComponent.html as template url
54 selector: 'app-ns-composer',
55 templateUrl: './NSComposerComponent.html',
56 styleUrls: ['./NSComposerComponent.scss'],
57 encapsulation: ViewEncapsulation.None
59 /** Exporting a class @exports NSComposerComponent */
60 export class NSComposerComponent {
61 /** To inject services @public */
62 public injector: Injector;
63 /** View child contains graphContainer ref @public */
64 @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef;
65 /** dataService to pass the data from one component to another @public */
66 public dataService: DataService;
67 /** Contains VNFD Informations @public */
68 public nsPackageDetails: NSData = { id: '', name: '', description: '', version: '', designer: '' };
69 /** Contains VL Details @public */
70 public virtualLinkDesc: VLD = {
74 /** Contains the information of the type of modification @public */
75 public putType: string;
76 /** Conatins mousedown action @public */
77 public mousedownNode: COMPOSERNODES = null;
78 /** Conatins mouseup action @public */
79 public mouseupNode: COMPOSERNODES = null;
80 /** Conatins mousedownLink action @public */
81 public mousedownLink: COMPOSERNODES = null;
82 /** Conatins current Selection node action @public */
83 public currentSelectedNode: COMPOSERNODES = null;
84 /** Conatins current Selection node action @public */
85 public currentSelectedLink: COMPOSERNODES = null;
86 /** Need to show the NSD Details @public */
87 public isShowNSDDetails: boolean = true;
88 /** Contains the node information of VL @public */
89 public vlNodes: {}[] = [];
90 /** Need to show the VL Details @public */
91 public isShowVLDetails: boolean = false;
92 /** Contains the node information of VNF @public */
93 public vnfNodes: {}[] = [];
94 /** contains the VNF Details @public */
95 public vnfData: VNFPROFILE;
96 /** contains the Virtual Link connectivity Details @public */
97 public virtualLinkProfileID: string;
98 /** Need to show the VNF Details @public */
99 public isShowVNFDetails: boolean = false;
100 /** Contains the node information of CP @public */
101 public cpNodes: {}[] = [];
102 /** Need to show the CP Details */
104 /** Need to show the VNF Details @public */
105 public isShowCPDetails: boolean = false;
106 /** random number count @public */
107 public randomNumberLength: number;
108 /** Contains the vnfd information @public */
109 public vnfList: VNFD[] = [];
110 /** Add the activeclass for the selected @public */
111 public activeClass: string = 'active';
112 /** Add the fixed class for the freeze @public */
113 public fixedClass: string = 'fixed';
114 /** Check the loading results @public */
115 public isLoadingResults: boolean = true;
116 /** Give the message for the loading @public */
117 public message: string = 'PLEASEWAIT';
118 /** Get VNF selected node @public */
119 public getVNFSelectedData: VNFD;
120 /** Assign the forcesimulation active @public */
121 public forceSimulationActive: boolean = false;
122 /** Assign pinned class for the button when freezed @public */
123 public classApplied: boolean = false;
124 /** Contains sidebar open status @public */
125 public sideBarOpened: boolean = false;
126 /** Contains SVG attributes @private */
127 // tslint:disable-next-line:no-any
129 /** Contains the Drag line */
130 // tslint:disable-next-line: no-any
131 private dragLine: any;
132 /** Contains VL node @private */
133 // tslint:disable-next-line:no-any
135 /** Contains VNFD node @private */
136 // tslint:disable-next-line:no-any
137 private vnfdnode: any;
138 /** Contains CP node @private */
139 // tslint:disable-next-line:no-any
141 /** Rendered nodes represent VL @private */
142 // tslint:disable-next-line:no-any
143 private gvlNode: any;
144 /** Rendered nodes represent VL @private */
145 // tslint:disable-next-line:no-any
146 private gvnfdNode: any;
147 /** Rendered nodes represent VL @private */
148 // tslint:disable-next-line:no-any
149 private gcpNode: any;
150 /** Contains forced node animations @private */
151 // tslint:disable-next-line:no-any
153 /** Contains all the selected node @private */
154 private selectedNode: COMPOSERNODES[] = [];
155 /** Contains the connected point @private */
156 private connectionPoint: string;
157 /** Contains id of the node @private */
158 private identifier: string;
159 /** Contains copy of NSD information @private */
160 private nsdCopy: string;
161 /** Contains the VNFD copy @private */
162 private vnfdCopy: string;
163 /** Contains path information of the node */
164 // tslint:disable-next-line:no-any
166 /** Contains the node information @private */
167 private nodes: COMPOSERNODES[] = [];
168 /** Contains the link information of nodes @private */
169 private links: {}[] = [];
170 /** Contains the NS information @private */
171 private nsData: NSDDetails;
172 /** Instance of the rest service @private */
173 private restService: RestService;
174 /** Service holds the router information @private */
175 private router: Router;
176 /** Holds teh instance of AuthService class of type AuthService @private */
177 private activatedRoute: ActivatedRoute;
178 /** Notifier service to popup notification @private */
179 private notifierService: NotifierService;
180 /** Controls the header form @private */
181 private headers: HttpHeaders;
182 /** Contains tranlsate instance @private */
183 private translateService: TranslateService;
184 /** Contains lastkeypressed instance @private */
185 private lastKeyDown: number = -1;
186 /** Instance of the modal service @private */
187 private modalService: NgbModal;
188 /** Setting the Value of connection point refrence of the CP @private */
189 private setVnfdConnectionPointRef: string;
190 /** Setting the Value of VL name for confirmation @private */
191 private vlName: string;
192 /** Setting the Value of VNFD name for confirmation @private */
193 private setVnfdName: string;
194 /** Contains all methods related to shared @private */
195 private sharedService: SharedService;
196 /** Contains selected node VNF profile objects @private */
197 private selectedVNFProfile: VNFPROFILE[];
199 constructor(injector: Injector) {
200 this.injector = injector;
201 this.restService = this.injector.get(RestService);
202 this.dataService = this.injector.get(DataService);
203 this.router = this.injector.get(Router);
204 this.activatedRoute = this.injector.get(ActivatedRoute);
205 this.notifierService = this.injector.get(NotifierService);
206 this.translateService = this.injector.get(TranslateService);
207 this.modalService = this.injector.get(NgbModal);
208 this.sharedService = this.injector.get(SharedService);
210 /** Lifecyle Hooks the trigger before component is instantiate @public */
211 public ngOnInit(): void {
212 // tslint:disable-next-line:no-backbone-get-set-outside-model
213 this.identifier = this.activatedRoute.snapshot.paramMap.get('id');
215 this.headers = new HttpHeaders({
216 'Content-Type': 'application/zip',
217 Accept: 'application/json',
218 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
221 /** Events handles at drag on D3 region @public */
222 // tslint:disable-next-line:no-any
223 public drag(ev: any): void {
224 if (ev.target.id === 'vl') {
225 ev.dataTransfer.setData('text', ev.target.id);
227 ev.dataTransfer.setData('text', ev.target.attributes['data-id'].value);
230 /** On clicking redirect to NS edit page @public */
231 public onEdit(): void {
232 this.router.navigate(['/packages/ns/edit/', this.identifier]).catch((): void => {
233 // Catch Navigation Error
236 /** Events handles drop at D3 region @public */
237 public drop(ev: DragEvent): void {
239 const getDropedName: string = ev.dataTransfer.getData('text');
240 if (getDropedName === 'vl') {
241 this.svg.selectAll('*').remove();
242 this.vldropComposer();
244 this.svg.selectAll('*').remove();
245 const vnfdName: string = ev.dataTransfer.getData('text');
246 this.vnfdropComposer(vnfdName);
249 /** Drop VL Composer Data @public */
250 public vldropComposer(): void {
251 this.randomNumberLength = CONSTANTNUMBER.randomNumber;
252 const generateId: string = 'ns_vl_' + this.sharedService.randomString();
253 if (this.nsData['virtual-link-desc'] === undefined) {
254 this.nsData['virtual-link-desc'] = [];
256 this.nsData['virtual-link-desc'].push({
258 'mgmt-network': false
260 this.putType = 'nsdadd';
261 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
263 /** Drop VNFD Composer Data @public */
264 public vnfdropComposer(vnfdName: string): void {
265 const vnfID: string = 'ns_vnfd_' + this.sharedService.randomString();
266 if (this.nsData.df.length > 0) {
267 this.addVNFDID(vnfdName);
268 this.nsData.df.forEach((res: DF): void => {
269 if (res['vnf-profile'] === undefined) {
270 res['vnf-profile'] = [];
272 res['vnf-profile'].push({
274 'virtual-link-connectivity': [],
279 Object.assign(this.nsData.df, {
283 'virtual-link-connectivity': [],
288 this.putType = 'vnfdadd';
289 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
291 /** Add the VNFD-ID while drop VNFD if not exists @public */
292 public addVNFDID(vnfdName: string): void {
293 const vnfdIDArray: string[] = this.nsData['vnfd-id'];
294 if (vnfdIDArray !== undefined) {
295 if (vnfdIDArray.indexOf(vnfdName) === -1) {
296 vnfdIDArray.push(vnfdName);
299 Object.assign(this.nsData, {
300 'vnfd-id': [vnfdName]
304 /** Events handles allow drop on D3 region @public */
305 public allowDrop(ev: DragEvent): void {
308 /** Save NSD Information @public */
309 public saveNSD(): void {
310 if (this.nsPackageDetails.id !== undefined) {
311 this.nsData.id = this.nsPackageDetails.id;
313 if (this.nsPackageDetails.name !== undefined) {
314 this.nsData.name = this.nsPackageDetails.name;
316 if (this.nsPackageDetails.description !== undefined) {
317 this.nsData.description = this.nsPackageDetails.description;
319 if (this.nsPackageDetails.version !== undefined) {
320 this.nsData.version = this.nsPackageDetails.version;
322 if (this.nsPackageDetails.designer !== undefined) {
323 this.nsData.designer = this.nsPackageDetails.designer;
325 this.putType = 'nsdUpdate';
326 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
328 /** Save Virtual Link @public */
329 public saveVL(vlid: string): void {
330 this.nsData['virtual-link-desc'].forEach((result: VLD): void => {
331 if (result.id === vlid) {
332 result['mgmt-network'] = !isNullOrUndefined(this.virtualLinkDesc['mgmt-network']) ? this.virtualLinkDesc['mgmt-network'] : true;
335 this.putType = 'vlUpdate';
336 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
338 /** Add the new Data @public */
339 public addData(apiURL: string, identifier: string, data: NSDDetails, putType: string): void {
340 this.isLoadingResults = true;
341 let successMessage: string = '';
342 if (putType === 'nsdadd') {
343 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNSD';
344 } else if (putType === 'vnfdadd') {
345 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDVNFD';
346 } else if (putType === 'cpAdded') {
347 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNS';
348 } else if (putType === 'nsdUpdate') {
349 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
350 } else if (putType === 'vlUpdate') {
351 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
352 } else if (putType === 'nsddelete') {
353 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENSD';
354 } else if (putType === 'vnfddelete') {
355 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETEVNFD';
356 } else if (putType === 'nsdelete') {
357 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENS';
358 } else if (putType === 'linkdelete') {
359 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETELINK';
361 /** Below hide for conflicts with light weight UI */
362 const apiURLHeader: APIURLHEADER = {
363 url: apiURL + '/' + identifier + '/nsd_content',
364 httpOptions: { headers: this.headers }
366 const nsData: NSDATACREATION = { nsd: { nsd: [] } };
368 nsData.nsd.nsd.push(data);
369 const descriptorInfo: string = jsyaml.dump(nsData, { sortKeys: true });
370 this.sharedService.targzFile({ packageType: 'nsd', id: this.identifier, descriptor: descriptorInfo })
371 .then((content: ArrayBuffer): void => {
372 this.restService.putResource(apiURLHeader, content).subscribe((res: {}): void => {
374 this.notifierService.notify('success', this.translateService.instant(successMessage));
375 this.isLoadingResults = false;
376 }, (error: ERRORDATA): void => {
378 this.restService.handleError(error, 'put');
379 this.isLoadingResults = false;
381 }).catch((): void => {
382 this.notifierService.notify('error', this.translateService.instant('ERROR'));
383 this.isLoadingResults = false;
386 /** Show Info @public */
387 public showInfo(): void {
388 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
389 modalRef.componentInstance.topologyType = 'Info';
390 modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.INFO');
391 modalRef.result.then((result: MODALCLOSERESPONSEDATA): void => {
397 /** Event to freeze the animation @public */
398 public onFreeze(): void {
399 this.classApplied = !this.classApplied;
400 const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass);
401 d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive);
402 if (alreadyFixedIsActive) {
405 this.forceSimulationActive = alreadyFixedIsActive;
406 this.nodes.forEach((d: COMPOSERNODES): void => {
407 d.fx = (alreadyFixedIsActive) ? null : d.x;
408 d.fy = (alreadyFixedIsActive) ? null : d.y;
410 if (alreadyFixedIsActive) {
411 this.force.restart();
414 /** Events handles when dragended @public */
415 public toggleSidebar(): void {
416 this.sideBarOpened = !this.sideBarOpened;
417 this.deselectAllNodes();
418 this.showRightSideInfo(true, false, false, false);
420 /** Prepare information for node creation of VNFD @private */
421 private generateData(): void {
422 this.generateVNFData();
423 this.generateDataNSDTopology();
424 this.sideBarOpened = false;
426 /** Prepare the information of the VNFD @private */
427 private generateVNFData(): void {
428 this.restService.getResource(environment.VNFPACKAGESCONTENT_URL).subscribe((vnfdPackageData: VNFD[]): void => {
429 this.vnfList = vnfdPackageData;
430 }, (error: ERRORDATA): void => {
431 this.restService.handleError(error, 'get');
434 /** Prepare information for node creation of NSD Topology @private */
435 private generateDataNSDTopology(): void {
438 this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL + '/' + this.identifier).subscribe((nsData: NSDDetails): void => {
439 delete nsData._admin;
441 delete nsData._links;
442 this.nsData = nsData;
443 this.generateNSInfo(nsData);
444 if (nsData['virtual-link-desc'] !== undefined) {
445 /** Details of the VL */
446 this.nsDataVLD(nsData);
448 if (this.nsData.df.length > 0) {
449 this.nsData.df.forEach((res: DF): void => {
450 if (res['vnf-profile'] !== undefined) {
451 /** Details of the VNFD */
452 this.nsDataConstituentVNFD(nsData);
456 if (nsData.df.length > 0) {
457 this.nsDataVLDLinkCreation(nsData);
459 this.separateAndCreatenode();
460 }, (error: ERRORDATA): void => {
461 if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
462 this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
464 this.restService.handleError(error, 'get');
466 this.isLoadingResults = false;
467 this.isShowNSDDetails = false;
470 /** Generate the NS Package Information */
471 private generateNSInfo(nsData: NSDDetails): void {
472 this.nsPackageDetails.id = nsData.id;
473 this.nsPackageDetails.name = nsData.name;
474 this.nsPackageDetails.description = nsData.description;
475 this.nsPackageDetails.version = nsData.version;
476 this.nsPackageDetails.designer = nsData.designer;
478 /** nsData VL node creation function @private */
479 private nsDataVLD(nsData: NSDDetails): void {
480 nsData['virtual-link-desc'].forEach((res: VLD): void => {
481 this.nodes.push({ id: res.id, reflexive: false, type: 'vld', name: res.id, selectorId: res.id });
484 /** nsData VNFD node creation function @private */
485 private nsDataConstituentVNFD(nsData: NSDDetails): void {
486 nsData.df.forEach((resDF: DF): void => {
487 resDF['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
490 id: resVNF['vnfd-id'] + ':' + resVNF.id,
493 name: resVNF['vnfd-id'],
494 nodeIndex: resVNF.id,
495 selectorId: resVNF['vnfd-id'] + '_' + resVNF.id
497 if (resVNF['virtual-link-connectivity'] !== undefined) {
498 this.nsDataCP(resVNF, resVNF.id);
503 /** nsData CP node creation function @private */
504 private nsDataCP(resVNF: VNFPROFILE, vnfID: string): void {
505 resVNF['virtual-link-connectivity'].forEach((resultVLC: VLC, index: number): void => {
506 resultVLC['constituent-cpd-id'].forEach((resultCCI: CCI): void => {
509 id: vnfID + ':' + resultCCI['constituent-base-element-id'] + index + ':' + resultCCI['constituent-cpd-id'],
512 name: resultCCI['constituent-cpd-id'],
513 nodeIndex: resultCCI['constituent-base-element-id'],
514 selectorId: 'osm-' + resultCCI['constituent-cpd-id'] + '-' + vnfID + resultCCI['constituent-base-element-id'] + index
519 /** nsData Link node creation function @private */
520 private nsDataVLDLinkCreation(nsData: NSDDetails): void {
521 nsData.df.forEach((resDF: DF): void => {
522 if (resDF['vnf-profile'] !== undefined) {
523 resDF['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
524 this.nsdCopy = resVNF['vnfd-id'] + ':' + resVNF.id;
525 if (resVNF['virtual-link-connectivity'] !== undefined) {
526 this.nsDataVNFDConnectionPointRefrence(resVNF);
532 /** nsDataVNFDConnectionPointRefrence undefined Call this function @private */
533 private nsDataVNFDConnectionPointRefrence(resVNF: VNFPROFILE): void {
534 resVNF['virtual-link-connectivity'].forEach((resultVLC: VLC, index: number): void => {
535 resultVLC['constituent-cpd-id'].forEach((resultCCI: CCI): void => {
536 this.vnfdCopy = resultVLC['virtual-link-profile-id'];
537 this.connectionPoint = resVNF.id + ':' + resultCCI['constituent-base-element-id'] + index + ':' + resultCCI['constituent-cpd-id'];
538 const connectionPointPos: number = this.nodes.map((e: COMPOSERNODES): string => { return e.id; }).indexOf(this.connectionPoint);
539 const nsdPos: number = this.nodes.map((e: COMPOSERNODES): string => { return e.id; }).indexOf(this.nsdCopy);
540 const vnfdPos: number = this.nodes.map((e: COMPOSERNODES): string => { return e.id; }).indexOf(this.vnfdCopy);
543 source: this.nodes[connectionPointPos],
544 target: this.nodes[nsdPos]
547 source: this.nodes[connectionPointPos],
548 target: this.nodes[vnfdPos]
553 /** Separate and create node @private */
554 private separateAndCreatenode(): void {
555 this.seprateNodes(this.nodes);
556 this.createnode(this.nodes);
557 this.isLoadingResults = false;
559 /** Get the default Configuration of containers @private */
560 private getGraphContainerAttr(): GRAPHDETAILS {
573 sourcePaddingYes: 17,
583 /** Separate the nodes along with its tyep @private */
584 private seprateNodes(node: COMPOSERNODES[]): void {
585 this.vlNodes = []; this.vnfNodes = []; this.cpNodes = [];
586 node.forEach((nodeList: COMPOSERNODES): void => {
587 if (nodeList.type === 'vld') {
588 this.vlNodes.push(nodeList);
589 } else if (nodeList.type === 'vnfd') {
590 this.vnfNodes.push(nodeList);
591 } else if (nodeList.type === 'ns') {
592 this.cpNodes.push(nodeList);
596 /** Node is created and render at D3 region @private */
597 private createnode(node: COMPOSERNODES[]): void {
598 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
599 d3.selectAll('svg#graphContainer > *').remove();
600 d3.select(window).on('keydown', (): void => { this.keyDown(); });
601 d3.select(window).on('keyup', (): void => { this.keyUp(); });
602 this.svg = d3.select('#graphContainer')
603 .attr('oncontextmenu', 'return false;')
604 .attr('viewBox', `0 0 ${graphContainerAttr.width} ${graphContainerAttr.height}`)
605 .on('mousemove', (): void => { this.mousemove(); });
606 this.force = d3.forceSimulation()
607 .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength))
608 .force('link', d3.forceLink().id((d: TickPath): string => d.id).distance(graphContainerAttr.distance))
609 .force('center', d3.forceCenter(graphContainerAttr.width / graphContainerAttr.forcex,
610 graphContainerAttr.height / graphContainerAttr.forcey))
611 .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex))
612 .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey))
613 .on('tick', (): void => { this.tick(); });
614 this.dragLine = this.svg.append('svg:path').attr('class', 'link dragline hidden').attr('d', 'M0,0L0,0');
615 this.path = this.svg.append('svg:g').selectAll('path');
616 this.vlNode = this.svg.append('svg:g').selectAll('vlnode');
617 this.vnfdnode = this.svg.append('svg:g').selectAll('vnfdnode');
618 this.cpnode = this.svg.append('svg:g').selectAll('cpnode');
622 /** update force layout (called automatically each iteration) @private */
623 private tick(): void {
624 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
625 // draw directed edges with proper padding from node centers
626 this.path.attr('class', 'link').attr('d', (d: Tick): string => {
627 const deltaX: number = d.target.x - d.source.x;
628 const deltaY: number = d.target.y - d.source.y;
629 const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
630 const normX: number = deltaX / dist;
631 const normY: number = deltaY / dist;
632 const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
633 const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
634 const sourceX: number = d.source.x + (sourcePadding * normX);
635 const sourceY: number = d.source.y + (sourcePadding * normY);
636 const targetX: number = d.target.x - (targetPadding * normX);
637 const targetY: number = d.target.y - (targetPadding * normY);
638 return `M${sourceX},${sourceY}L${targetX},${targetY}`;
639 }).on('dblclick', (d: Tick): void => { this.getDeleteLinkConfirmation(d); });
640 this.vlNode.attr('transform', (t: TickPath): string => `translate(${t.x},${t.y})`);
641 this.vnfdnode.attr('transform', (t: TickPath): string => `translate(${t.x},${t.y})`);
642 this.cpnode.attr('transform', (t: TickPath): string => `translate(${t.x},${t.y})`);
644 /** Update graph (called when needed) at D3 region @private */
645 private restart(node: COMPOSERNODES[]): void {
646 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
647 this.path = this.path.data(this.links);
648 this.vlNode = this.vlNode.data(this.vlNodes, (d: COMPOSERNODES): string => d.id);
649 this.vnfdnode = this.vnfdnode.data(this.vnfNodes, (d: COMPOSERNODES): string => d.id);
650 this.cpnode = this.cpnode.data(this.cpNodes, (d: COMPOSERNODES): string => d.id);
651 this.resetAndCreateNodes();
652 this.force.nodes(node).force('link').links(this.links);
653 this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
655 /** Rest and create nodes @private */
656 private resetAndCreateNodes(): void {
657 this.path.exit().remove();
658 this.vlNode.exit().remove();
659 this.vnfdnode.exit().remove();
660 this.cpnode.exit().remove();
665 this.path.merge(this.path);
666 this.vlNode = this.gvlNode.merge(this.vlNode);
667 this.vnfdnode = this.gvnfdNode.merge(this.vnfdnode);
668 this.cpnode = this.gcpNode.merge(this.cpnode);
670 /** setting the Path @private */
671 private getPathNodes(): void {
672 this.path = this.path.enter().append('svg:path');
674 /** Setting all the VL nodes @private */
675 private getVLNodes(): void {
676 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
677 this.gvlNode = this.vlNode.enter().append('svg:g');
678 this.gvlNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
679 this.gvlNode.append('svg:image')
681 .attr('x', graphContainerAttr.imageX)
682 .attr('y', graphContainerAttr.imageY)
683 .call(this.onDragDrop())
684 .attr('id', (d: COMPOSERNODES): string => { return d.selectorId; })
685 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
686 .attr('xlink:href', 'assets/images/VL.svg')
687 .on('mousedown', (d: COMPOSERNODES): void => { this.mouseDown(d); })
688 .on('mouseup', (d: COMPOSERNODES): void => { this.mouseUp(d); })
689 .on('click', (d: COMPOSERNODES): void => { this.singleClick(this.vlNode, d); this.onNodeClickToggleSidebar(); })
690 .on('dblclick', (d: COMPOSERNODES): void => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
691 this.gvlNode.append('svg:text')
692 .attr('class', 'node_text')
693 .attr('y', graphContainerAttr.textY)
694 .text((d: COMPOSERNODES): string => d.id);
696 /** Setting all the VNFD nodes @private */
697 private getVNFDNodes(): void {
698 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
699 this.gvnfdNode = this.vnfdnode.enter().append('svg:g');
700 this.gvnfdNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
701 this.gvnfdNode.append('svg:image')
703 .attr('x', graphContainerAttr.imageX)
704 .attr('y', graphContainerAttr.imageY)
705 .call(this.onDragDrop())
706 .attr('id', (d: COMPOSERNODES): string => { return d.selectorId; })
707 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
708 .attr('xlink:href', 'assets/images/VNFD.svg')
709 .on('mousedown', (d: COMPOSERNODES): void => { this.mouseDown(d); })
710 .on('mouseup', (d: COMPOSERNODES): void => { this.mouseUp(d); })
711 .on('click', (d: COMPOSERNODES): void => { this.singleClick(this.vnfdnode, d); this.onNodeClickToggleSidebar(); })
712 .on('dblclick', (d: COMPOSERNODES): void => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
713 this.gvnfdNode.append('svg:text')
714 .attr('class', 'node_text')
715 .attr('y', graphContainerAttr.textY)
716 .text((d: COMPOSERNODES): string => d.name);
718 /** Setting all the CP nodes @private */
719 private getCPNodes(): void {
720 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
721 this.gcpNode = this.cpnode.enter().append('svg:g');
722 this.gcpNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
723 this.gcpNode.append('svg:image')
725 .attr('x', graphContainerAttr.imageX)
726 .attr('y', graphContainerAttr.imageY)
727 .call(this.onDragDrop())
728 .attr('id', (d: COMPOSERNODES): string => { return d.selectorId; })
729 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
730 .attr('xlink:href', 'assets/images/CP.svg')
731 .on('mousedown', (d: COMPOSERNODES): void => { this.mouseDown(d); })
732 .on('mouseup', (d: COMPOSERNODES): void => { this.mouseUp(d); })
733 .on('click', (d: COMPOSERNODES): void => { this.singleClick(this.cpnode, d); this.onNodeClickToggleSidebar(); })
734 .on('dblclick', (d: COMPOSERNODES): void => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
735 this.gcpNode.append('svg:text')
736 .attr('class', 'node_text')
737 .attr('y', graphContainerAttr.textY)
738 .text((d: COMPOSERNODES): string => d.name);
740 /** Events handles when mousemove it will capture the selected node data @private */
741 private mousemove(): void {
742 if (!this.mousedownNode) { return; }
743 this.dragLine.attr('d',
744 `M${this.mousedownNode.x},${this.mousedownNode.y}L${d3.mouse(d3.event.currentTarget)[0]},${d3.mouse(d3.event.currentTarget)[1]}`);
746 /** Get confirmation Before Deleting the Link in Topology @private */
747 private getAddConfirmation(mouseData: COMPOSERNODES, getNsData: NSDDetails, addType: string, getVLDIndex: string): void {
748 let findVNFName: string = '';
749 let findVLDID: string = '';
750 if (mouseData.type === 'vld') {
751 findVNFName = this.mouseupNode.name;
752 findVLDID = this.mousedownNode.id;
753 this.vlName = this.mousedownNode.name;
755 findVNFName = this.mousedownNode.name;
756 findVLDID = this.mouseupNode.id;
757 this.vlName = this.mouseupNode.name;
759 if (getNsData['vnfd-id'] !== undefined) {
760 getNsData['vnfd-id'].forEach((resVNFid: string): void => {
761 if (resVNFid === findVNFName) {
762 this.getVNFSelectedData = this.vnfList.filter((vnfList: VNFD): boolean => vnfList.id === findVNFName)[0];
763 this.setVnfdConnectionPointRef = this.getVNFSelectedData['mgmt-cp'];
764 this.setVnfdName = this.getVNFSelectedData['product-name'];
765 this.selectedVNFProfile = getNsData.df[0]['vnf-profile'];
769 if (this.vlName !== undefined && this.setVnfdName !== undefined && this.setVnfdConnectionPointRef !== undefined) {
770 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
771 modalRef.componentInstance.topologyType = 'Add';
772 modalRef.componentInstance.cpDetails = this.getVNFSelectedData['ext-cpd'];
773 this.translateService.get('PAGE.TOPOLOGY.ADDINGCP', {
774 vlname: '<b>' + this.vlName + '</b>',
775 vnfdname: '<b>' + this.setVnfdName + '</b>',
776 cpname: '<b>' + this.setVnfdConnectionPointRef + '</b>'
777 }).subscribe((res: string): void => {
778 modalRef.componentInstance.topologyname = res;
780 modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.CONNECTIONPOINT');
781 modalRef.result.then((result: MODALCLOSERESPONSEWITHCP): void => {
783 this.generateCPForVNF(this.selectedVNFProfile, result.connection_point, getVLDIndex);
784 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, getNsData, addType);
791 this.notifierService.notify('error', this.translateService.instant('ERROR'));
794 /** Generate connection point for vnf using vld @private */
795 private generateCPForVNF(result: VNFPROFILE[], cp: string, getVLDIndex: string): void {
796 if (result !== undefined) {
797 result.forEach((resultVNFPROFILE: VNFPROFILE, index: number): void => {
798 if (getVLDIndex === resultVNFPROFILE.id) {
799 resultVNFPROFILE['virtual-link-connectivity'].push({
800 'constituent-cpd-id': [{
801 'constituent-base-element-id': getVLDIndex,
802 'constituent-cpd-id': cp
804 'virtual-link-profile-id': this.vlName
809 Object.assign(result, {
810 'virtual-link-connectivity': [{
811 'constituent-cpd-id': [{
812 'constituent-base-element-id': getVLDIndex,
813 'constituent-cpd-id': cp
815 'virtual-link-profile-id': this.vlName
820 /** Events handles when mousedown click it will capture the selected node data @private */
821 private mouseDown(d: COMPOSERNODES): void {
822 event.preventDefault();
823 if (d3.event.ctrlKey) { return; }
824 if (d3.event.shiftKey) {
825 if (d.type === 'vnfd') {
826 this.selectedNode.push(d);
828 this.mousedownNode = d;
829 this.currentSelectedNode = (this.mousedownNode === this.currentSelectedNode) ? null : this.mousedownNode;
830 this.currentSelectedLink = null;
831 this.dragLine.style('marker-end', 'url(#end-arrow)').classed('hidden', false)
832 .attr('d', `M${this.mousedownNode.x},${this.mousedownNode.y}L${this.mousedownNode.x},${this.mousedownNode.y}`);
835 /** Event handles when mouseup event occures @private */
836 private mouseUp(d: COMPOSERNODES): void {
837 if (!this.mousedownNode) { return; }
838 this.dragLine.classed('hidden', true).style('marker-end', '');
839 this.mouseupNode = d;
840 if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vnfd') {
841 const getOldVLDIndex: string[] = this.mouseupNode.id.split(':');
842 const setOldVLDindex: string = getOldVLDIndex[1];
843 this.putType = 'cpAdded';
844 this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
845 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vld') {
846 const getOldVLDIndex: string[] = this.mousedownNode.id.split(':');
847 const setOldVLDindex: string = getOldVLDIndex[1];
848 this.putType = 'cpAdded';
849 this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
850 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'ns') {
852 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
853 } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'ns') {
855 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
856 } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vld') {
858 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVL'));
859 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vnfd') {
861 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNF'));
862 } else if (this.mousedownNode.type === 'ns' && this.mouseupNode.type === 'ns') {
864 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKCP'));
867 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVLVNF'));
869 this.resetMouseVars();
871 this.currentSelectedLink = d;
872 this.currentSelectedNode = null;
874 /** Mosue Drag Line false if it is not satisfied @private */
875 private deselectPath(): void {
876 this.dragLine.classed('hidden', true).style('marker-end', '').attr('d', 'M0,0L0,0');
878 /** reset Mouse varaibles @private */
879 private resetMouseVars(): void {
880 this.mousedownNode = null;
881 this.mouseupNode = null;
882 this.mousedownLink = null;
884 /** De-select all the selected nodes @private */
885 private deselectAllNodes(): void {
886 this.vlNode.select('image').classed(this.activeClass, false);
887 this.vnfdnode.select('image').classed(this.activeClass, false);
888 this.cpnode.select('image').classed(this.activeClass, false);
890 /** Show the right-side information @private */
891 private showRightSideInfo(nsdDetails: boolean, vldDetails: boolean, vnfDeails: boolean, cpDetails: boolean): void {
892 this.isShowNSDDetails = nsdDetails;
893 this.isShowVLDetails = vldDetails;
894 this.isShowVNFDetails = vnfDeails;
895 this.isShowCPDetails = cpDetails;
897 /** Events handles when Shift Click to perform create cp @private */
898 // tslint:disable-next-line: no-any
899 private singleClick(nodeSelected: any, d: COMPOSERNODES): void {
900 this.selectNodeExclusive(nodeSelected, d);
902 /** Selected nodes @private */
903 // tslint:disable-next-line: no-any
904 private selectNodeExclusive(nodeSeleced: any, d: COMPOSERNODES): void {
905 const alreadyIsActive: boolean = nodeSeleced.select('#' + d.selectorId).classed(this.activeClass);
906 this.deselectAllNodes();
907 nodeSeleced.select('#' + d.selectorId).classed(this.activeClass, !alreadyIsActive);
908 if (d.type === 'vld' && !alreadyIsActive) {
909 this.nsData['virtual-link-desc'].forEach((result: VLD): void => {
910 if (result.id === d.id) {
911 this.showRightSideInfo(false, true, false, false);
912 this.virtualLinkDesc = result;
915 } else if (d.type === 'vnfd' && !alreadyIsActive) {
916 this.nsData.df.forEach((res: DF): void => {
917 if (res['vnf-profile'] !== undefined) {
918 res['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
919 if (resVNF.id === d.nodeIndex && resVNF['vnfd-id'] === d.name) {
920 this.showRightSideInfo(false, false, true, false);
921 this.vnfData = resVNF;
926 } else if (d.type === 'ns' && !alreadyIsActive) {
927 this.nsData.df.forEach((resultDF: DF): void => {
928 if (resultDF['vnf-profile'] !== undefined) {
929 resultDF['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
930 if (resVNF['virtual-link-connectivity'] !== undefined) {
931 resVNF['virtual-link-connectivity'].forEach((resultVLC: VLC, index: number): void => {
932 resultVLC['constituent-cpd-id'].forEach((resultCCI: CCI): void => {
933 const connectionPointID: string = resVNF.id + ':' + resultCCI['constituent-base-element-id'] + index + ':' + resultCCI['constituent-cpd-id'];
934 if (connectionPointID === d.id) {
935 this.cpData = resultCCI;
936 this.vnfData = resVNF;
937 this.virtualLinkProfileID = resultVLC['virtual-link-profile-id'];
938 this.showRightSideInfo(false, false, false, true);
947 this.showRightSideInfo(true, false, false, false);
950 /** Get confirmation Before Deleting the Link in Topology @private */
951 private getDeleteLinkConfirmation(d: Tick): void {
952 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
953 modalRef.componentInstance.topologyType = 'Delete';
954 modalRef.componentInstance.topologyname = this.translateService.instant('PAGE.TOPOLOGY.LINK') + ' - ' + d.source.id;
955 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.LINK';
956 modalRef.result.then((result: MODALCLOSERESPONSEDATA): void => {
958 this.doubleClickLink(d);
962 /** Events handles when Double Click to Delete the link @private */
963 private doubleClickLink(d: Tick): void {
964 let getID: string = '';
965 let getName: string = '';
966 let getNodeIndex: string;
967 if (d.source.type === 'ns') {
969 getName = d.source.name;
970 getNodeIndex = d.source.nodeIndex;
972 /** Split the selected node of connectionpoint */
973 const selectedNode: string[] = getID.split(':');
974 this.nsData.df.forEach((resultDF: DF): void => {
975 if (resultDF['vnf-profile'] !== undefined) {
976 resultDF['vnf-profile'].forEach((elementVNF: VNFPROFILE): void => {
977 const selectedVNFProfileID: string = selectedNode[0];
978 /** If VNF ID is equal to selected VNFProfile ID check the VLC of CCI to match the id and name to remove the VLC index */
979 if (selectedVNFProfileID === elementVNF.id) {
980 elementVNF['virtual-link-connectivity'].forEach((elementVLC: VLC, index: number): void => {
981 const posCCI: number = elementVLC['constituent-cpd-id'].findIndex((e: CCI): boolean => {
982 const getCID: string = elementVNF.id + ':' + e['constituent-base-element-id'] + index + ':' + e['constituent-cpd-id'];
983 return getID === getCID;
986 elementVNF['virtual-link-connectivity'].splice(index, 1);
993 this.putType = 'linkdelete';
994 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
996 /** Get confirmation Before Deleting the Node in Topology @private */
997 private getDeleteConfirmation(d: COMPOSERNODES): void {
998 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
999 modalRef.componentInstance.topologyType = 'Delete';
1000 modalRef.componentInstance.topologyname = d.name;
1001 if (d.type === 'vld') {
1002 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VIRTUALLINK';
1003 } else if (d.type === 'vnfd') {
1004 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VNF';
1005 } else if (d.type === 'ns') {
1006 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.CONNECTIONPOINT';
1008 modalRef.result.then((result: MODALCLOSERESPONSEDATA): void => {
1010 this.doubleClick(d);
1014 /** Events handles when Double Click to Delete @private */
1015 private doubleClick(d: COMPOSERNODES): void {
1016 const deletedNode: COMPOSERNODES = d;
1017 this.nodes.forEach((res: COMPOSERNODES, i: number): void => {
1018 if (res.id === d.id) {
1019 if (deletedNode.type === 'vld') {
1020 /** Remove the virtual-link-desc related to VL */
1021 const pos: number = this.nsData['virtual-link-desc'].map((e: VLD): string => { return e.id; }).indexOf(d.id);
1022 this.nsData['virtual-link-desc'].splice(pos, 1);
1023 /** Remove the virtual-link-connectivity between VL and VNFD */
1024 this.nsData.df.forEach((resultDF: DF): void => {
1025 if (resultDF['vnf-profile'] !== undefined) {
1026 resultDF['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
1027 const getVLArray: number[] = resVNF['virtual-link-connectivity'].map((e: VLC, index: number): number => {
1028 if (e['virtual-link-profile-id'] === d.id) {
1032 if (getVLArray.length > 0) {
1033 getVLArray.forEach((removeIndex: number): void => {
1034 const index: string = removeIndex.toString();
1035 resVNF['virtual-link-connectivity'].splice(resVNF['virtual-link-connectivity'][index], 1);
1041 this.putType = 'nsddelete';
1042 } else if (deletedNode.type === 'vnfd') {
1043 this.nsData.df.forEach((resultDF: DF): void => {
1044 if (resultDF['vnf-profile'] !== undefined) {
1045 /** Remove the vnf-profile related to VNFD */
1046 const posVNF: number = resultDF['vnf-profile'].findIndex((e: VNFPROFILE): boolean => {
1047 return e['vnfd-id'] === d.name && e.id === d.nodeIndex;
1049 resultDF['vnf-profile'].splice(posVNF, 1);
1050 /** Check the VNFD exists in any vnf-profile */
1051 const isVNFDExists: boolean = resultDF['vnf-profile'].some((e: VNFPROFILE): boolean => {
1052 return e['vnfd-id'] === d.name;
1054 /** If VNFD not exists in the vnf-profile remove from vnfd-id */
1055 if (!isVNFDExists) {
1056 const posVNFD: number = this.nsData['vnfd-id'].findIndex((e: string): boolean => {
1057 return e === d.name;
1059 this.nsData['vnfd-id'].splice(posVNFD, 1);
1063 this.putType = 'vnfddelete';
1064 } else if (deletedNode.type === 'ns') {
1065 /** Split the selected node */
1066 const selectedNode: string[] = d.id.split(':');
1067 this.nsData.df.forEach((resultDF: DF): void => {
1068 if (resultDF['vnf-profile'] !== undefined) {
1069 resultDF['vnf-profile'].forEach((elementVNF: VNFPROFILE): void => {
1070 const selectedVNFProfileID: string = selectedNode[0];
1071 /** If VNF ID is equal to selected VNFProfile ID check the VLC of CCI to match the id and name to remove the VLC index */
1072 if (selectedVNFProfileID === elementVNF.id) {
1073 elementVNF['virtual-link-connectivity'].forEach((elementVLC: VLC, index: number): void => {
1074 const posCCI: number = elementVLC['constituent-cpd-id'].findIndex((e: CCI): boolean => {
1075 const getID: string = elementVNF.id + ':' + e['constituent-base-element-id'] + index + ':' + e['constituent-cpd-id'];
1076 return d.id === getID;
1078 if (posCCI !== -1) {
1079 elementVNF['virtual-link-connectivity'].splice(index, 1);
1086 this.putType = 'nsdelete';
1088 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
1092 /** drag event @private */
1093 // tslint:disable-next-line: no-any
1094 private onDragDrop(): any {
1095 return d3.drag().filter(this.dragFilter)
1096 .on('start', this.dragstarted)
1097 .on('drag', this.dragged)
1098 .on('end', this.dragended);
1100 /** Key press event @private */
1101 private keyDown(): void {
1102 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
1103 if (this.lastKeyDown !== -1) { return; }
1104 this.lastKeyDown = d3.event.keyCode;
1105 if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
1106 this.svg.classed('ctrl', true);
1109 /** Key realse event @private */
1110 private keyUp(): void {
1111 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
1112 this.lastKeyDown = -1;
1113 if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
1114 this.gvlNode.on('.drag', null);
1115 this.gvnfdNode.on('.drag', null);
1116 this.gcpNode.on('.drag', null);
1117 this.svg.classed('ctrl', false);
1120 /** Events handles when to drag using filter for the keys @private */
1121 private dragFilter(): boolean {
1122 return d3.event.ctrlKey && !d3.event.button;
1124 /** Events handles when dragstarted @private */
1125 private dragstarted(d: COMPOSERNODES): void {
1129 /** Events handles when dragged @private */
1130 private dragged(d: COMPOSERNODES): void {
1131 d.fx = d.x = d3.event.x;
1132 d.fy = d.y = d3.event.y;
1134 /** Events handles when dragended @private */
1135 private dragended(d: COMPOSERNODES): void {
1136 if (this.forceSimulationActive) {
1142 this.forceSimulationActive = false;
1145 /** Events handles when node double click @private */
1146 private onNodedblClickToggleSidebar(): void {
1147 this.sideBarOpened = false;
1149 /** Events handles when node single click @private */
1150 private onNodeClickToggleSidebar(): void {
1151 this.sideBarOpened = true;