44c63094d819308bd1115c8ba103fa7bfd520b6d
[osm/NG-UI.git] / src / app / instances / ns-topology / NSTopologyComponent.ts
1 /*
2  Copyright 2020 TATA ELXSI
3
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
7
8   http://www.apache.org/licenses/LICENSE-2.0
9
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.
15
16  Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
17  */
18 /**
19  * @file NS Topology Component
20  */
21 /* tslint:disable:no-increment-decrement */
22 import { Component, ElementRef, Injector, ViewChild, ViewEncapsulation } from '@angular/core';
23 import { ActivatedRoute } from '@angular/router';
24 import { Router } from '@angular/router';
25 import { TranslateService } from '@ngx-translate/core';
26 import { ERRORDATA } from 'CommonModel';
27 import * as d3 from 'd3';
28 import { environment } from 'environment';
29 import * as HttpStatus from 'http-status-codes';
30 import { VNFDCONNECTIONPOINTREF } from 'NSDModel';
31 import { COMPOSERNODES, CONNECTIONPOINT, NSD, NSDVLD, NSINFO, NSInstanceDetails, NSINSTANCENODES, VLINFO, VNFRINFO } from 'NSInstanceModel';
32 import { GRAPHDETAILS, Tick, TickPath } from 'NSTopologyModel';
33 import { RestService } from 'src/services/RestService';
34 import { isNullOrUndefined } from 'util';
35
36 /**
37  * Creating component
38  * @Component takes NSTopologyComponent.html as template url
39  */
40 @Component({
41   selector: 'app-ns-topology',
42   templateUrl: './NSTopologyComponent.html',
43   styleUrls: ['./NSTopologyComponent.scss'],
44   encapsulation: ViewEncapsulation.None
45 })
46 /** Exporting a class @exports NSTopologyComponent */
47 export class NSTopologyComponent {
48   /** Injector to invoke other services @public */
49   public injector: Injector;
50   /** View child contains graphContainer ref @public  */
51   @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef;
52   /** Holds the basic information of NS @public */
53   public nsInfo: NSINFO;
54   /** Contains tranlsate instance @private */
55   public translateService: TranslateService;
56   /** Add the activeclass for the selected @public */
57   public activeClass: string = 'active';
58   /** Add the fixed class for the freeze @public */
59   public fixedClass: string = 'fixed';
60   /** Check the loading results @public */
61   public isLoadingResults: boolean = true;
62   /** Give the message for the loading @public */
63   public message: string = 'PLEASEWAIT';
64   /** Assign the forcesimulation active @public */
65   public forceSimulationActive: boolean = false;
66   /** Assign pinned class for the button when freezed @public */
67   public classApplied: boolean = false;
68   /** Contains sidebar open status @public */
69   public sideBarOpened: boolean = true;
70   /** Need to show the NS Details @public */
71   public isShowNSDetails: boolean = true;
72   /** Need to show the VL Details @public */
73   public isShowVLetails: boolean = false;
74   /** Need to show the VNFR Details @public */
75   public isShowVNFRDetails: boolean = false;
76   /** Show right side info of Virtual Link @public */
77   public virtualLink: VLINFO;
78   /** Show right side info of Virtual Link @public */
79   public vnfr: VNFRINFO;
80
81   /** Contains lastkeypressed instance @private */
82   private lastKeyDown: number = -1;
83   /** Instance of the rest service @private */
84   private restService: RestService;
85   /** Holds the instance of AuthService class of type AuthService @private */
86   private activatedRoute: ActivatedRoute;
87   /** Holds the NS Id @private */
88   private nsIdentifier: string;
89   /** Contains SVG attributes @private */
90   // tslint:disable-next-line:no-any
91   private svg: any;
92   /** Contains forced node animations @private */
93   // tslint:disable-next-line:no-any
94   private force: any;
95   /** Contains path information of the node */
96   // tslint:disable-next-line:no-any
97   private path: any;
98   /** Contains node network @private */
99   // tslint:disable-next-line:no-any
100   private network: any;
101   /** Contains node square @private */
102   // tslint:disable-next-line:no-any
103   private square: any;
104   /** Contains node circle @private */
105   // tslint:disable-next-line:no-any
106   private circle: any;
107   /** Contains the NS information @private */
108   private nsData: NSInstanceDetails;
109   /** Contains NDS information of a descriptors */
110   private nsdData: NSD;
111   /** Contains node information @private */
112   private nodes: NSINSTANCENODES[] = [];
113   /** Contains links information @private */
114   private links: {}[] = [];
115   /** holds cp count/iteration @private */
116   private cpCount: number;
117   /** VNFD nodes @private */
118   private vnfdNodes: {}[] = [];
119   /** VLD nodes @private */
120   private vldNodes: {}[] = [];
121   /** Connection CP nodes @private */
122   private cpNodes: {}[] = [];
123   /** Set timeout @private */
124   private TIMEOUT: number = 2000;
125   /** Rendered nodes represent vnf @private */
126   // tslint:disable-next-line:no-any
127   private gSquare: any;
128   /** Rendered nodes represent network @private */
129   // tslint:disable-next-line:no-any
130   private gNetwork: any;
131   /** Rendered nodes represent network @private */
132   // tslint:disable-next-line:no-any
133   private gCircle: any;
134   /** Service holds the router information @private */
135   private router: Router;
136
137   constructor(injector: Injector) {
138     this.injector = injector;
139     this.restService = this.injector.get(RestService);
140     this.activatedRoute = this.injector.get(ActivatedRoute);
141     this.translateService = this.injector.get(TranslateService);
142     this.router = this.injector.get(Router);
143   }
144
145   /**
146    * Lifecyle Hooks the trigger before component is instantiate @public
147    */
148   public ngOnInit(): void {
149     // tslint:disable-next-line:no-backbone-get-set-outside-model
150     this.nsIdentifier = this.activatedRoute.snapshot.paramMap.get('id');
151     this.generateData();
152   }
153   /** Event to freeze the animation @public */
154   public onFreeze(): void {
155     this.classApplied = !this.classApplied;
156     const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass);
157     d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive);
158     if (alreadyFixedIsActive) {
159       this.force.stop();
160     }
161     this.forceSimulationActive = alreadyFixedIsActive;
162     this.nodes.forEach((d: COMPOSERNODES) => {
163       d.fx = (alreadyFixedIsActive) ? null : d.x;
164       d.fy = (alreadyFixedIsActive) ? null : d.y;
165     });
166     if (alreadyFixedIsActive) {
167       this.force.restart();
168     }
169   }
170   /** Events handles when dragended @public */
171   public toggleSidebar(): void {
172     this.sideBarOpened = !this.sideBarOpened;
173     this.deselectAllNodes();
174     this.showRightSideInfo(true, false, false);
175   }
176   /** Get the default Configuration of containers @private */
177   private getGraphContainerAttr(): GRAPHDETAILS {
178     return {
179       width: 700,
180       height: 400,
181       nodeHeight: 50,
182       nodeWidth: 35,
183       textX: -35,
184       textY: 30,
185       radius: 5,
186       distance: 50,
187       strength: -500,
188       forcex: 2,
189       forcey: 2,
190       sourcePaddingYes: 17,
191       sourcePaddingNo: 12,
192       targetPaddingYes: 4,
193       targetPaddingNo: 3,
194       alphaTarget: 0.3,
195       imageX: -25,
196       imageY: -25,
197       shiftKeyCode: 17
198     };
199   }
200   /** Show the right-side information @private */
201   private showRightSideInfo(nsDetails: boolean, vlDetails: boolean, vnfrDeails: boolean): void {
202     this.isShowNSDetails = nsDetails;
203     this.isShowVLetails = vlDetails;
204     this.isShowVNFRDetails = vnfrDeails;
205   }
206   /** De-select all the selected nodes @private */
207   private deselectAllNodes(): void {
208     this.square.select('image').classed(this.activeClass, false);
209     this.network.select('image').classed(this.activeClass, false);
210     this.circle.select('image').classed(this.activeClass, false);
211   }
212   /** Prepare all the information for node creation @private */
213   private generateData(): void {
214     this.restService.getResource(environment.NSINSTANCESCONTENT_URL + '/' + this.nsIdentifier).subscribe((nsData: NSInstanceDetails) => {
215       this.nsData = nsData;
216       this.nsInfo = {
217         nsInstanceID: nsData._id,
218         nsName: nsData.name,
219         nsOperationalStatus: nsData['operational-status'],
220         nsConfigStatus: nsData['config-status'],
221         nsDetailedStatus: nsData['detailed-status'],
222         nsResourceOrchestrator: nsData['resource-orchestrator']
223       };
224       if (this.nsData['constituent-vnfr-ref'] !== undefined) {
225         this.generateVNFRCPNodes();
226       }
227       if (this.nsData.vld !== undefined) {
228         this.generateVLDNetworkNodes();
229       }
230       setTimeout(() => {
231         this.pushAllNodes();
232         this.generateVNFDCP();
233         this.generateVLDCP();
234         this.isLoadingResults = false;
235         this.createNode(this.nodes, this.links);
236       }, this.TIMEOUT);
237     }, (error: ERRORDATA) => {
238       this.isLoadingResults = false;
239       if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
240         this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
241       } else {
242         this.restService.handleError(error, 'get');
243       }
244     });
245   }
246
247   /** Fetching all the VNFR Information @private */
248   private generateVNFRCPNodes(): void {
249     this.nsData['constituent-vnfr-ref'].forEach((vnfdrID: string) => {
250       this.restService.getResource(environment.VNFINSTANCES_URL + '/' + vnfdrID).subscribe((vndfrDetail: NSD) => {
251         this.nodes.push({
252           id: vndfrDetail['vnfd-ref'] + ':' + vndfrDetail['member-vnf-index-ref'],
253           nodeTypeRef: 'vnfd',
254           cp: vndfrDetail['connection-point'],
255           vdur: vndfrDetail.vdur,
256           vld: vndfrDetail.vld,
257           nsID: vndfrDetail['nsr-id-ref'],
258           vnfdID: vndfrDetail['vnfd-id'],
259           vimID: vndfrDetail['vim-account-id'],
260           vndfrID: vndfrDetail.id,
261           ipAddress: vndfrDetail['ip-address'],
262           memberIndex: vndfrDetail['member-vnf-index-ref'],
263           vnfdRef: vndfrDetail['vnfd-ref'],
264           selectorId: 'nsInst-' + vndfrDetail.id
265         });
266         // Fetching all the connection point of VNF & Interface
267         vndfrDetail['connection-point'].forEach((cp: CONNECTIONPOINT) => {
268           this.nodes.push({
269             id: cp.name + ':' + vndfrDetail['member-vnf-index-ref'],
270             vndfCPRef: vndfrDetail['vnfd-ref'] + ':' + vndfrDetail['member-vnf-index-ref'],
271             nodeTypeRef: 'cp',
272             name: cp.name
273           });
274         });
275       }, (error: ERRORDATA) => {
276         this.restService.handleError(error, 'get');
277       });
278     });
279   }
280
281   /** Fetching all the VLD/Network Information @private */
282   private generateVLDNetworkNodes(): void {
283     this.nsdData = this.nsData.nsd;
284     this.nsdData.vld.forEach((ref: NSDVLD) => {
285       this.nodes.push({
286         id: ref.id,
287         nodeTypeRef: 'vld',
288         name: ref.name,
289         type: ref.type,
290         vnfdCP: ref['vnfd-connection-point-ref'],
291         vimNetworkName: ref['vim-network-name'],
292         shortName: ref['short-name'],
293         selectorId: 'nsInst-' + ref.id
294       });
295     });
296   }
297
298   /** Pushing connection points of path/links nodes @private */
299   private pushAllNodes(): void {
300     this.nodes.forEach((nodeList: NSINSTANCENODES) => {
301       if (nodeList.nodeTypeRef === 'vnfd') {
302         this.vnfdNodes.push(nodeList);
303       } else if (nodeList.nodeTypeRef === 'vld') {
304         this.vldNodes.push(nodeList);
305       } else if (nodeList.nodeTypeRef === 'cp') {
306         this.cpNodes.push(nodeList);
307       }
308     });
309   }
310
311   /** Get CP position based on vndf @private */
312   private generateVNFDCP(): void {
313     this.vnfdNodes.forEach((list: NSINSTANCENODES) => {
314       const vndfPos: number = this.nodes.map((e: NSINSTANCENODES) => { return e.id; }).indexOf(list.id);
315       this.cpCount = 0;
316       this.nodes.forEach((res: NSINSTANCENODES) => {
317         if (res.nodeTypeRef === 'cp' && res.vndfCPRef === list.id) {
318           this.links.push({ source: this.nodes[vndfPos], target: this.nodes[this.cpCount] });
319         }
320         this.cpCount++;
321       });
322     });
323   }
324
325   /** Get CP position based on vld @private */
326   private generateVLDCP(): void {
327     let vldPos: number = 0;
328     this.vldNodes.forEach((list: NSINSTANCENODES) => {
329       if (!isNullOrUndefined(list.vnfdCP)) {
330         list.vnfdCP.forEach((cpRef: VNFDCONNECTIONPOINTREF) => {
331           this.cpCount = 0;
332           this.nodes.forEach((res: NSINSTANCENODES) => {
333             if (res.nodeTypeRef === 'cp' && res.id === cpRef['vnfd-connection-point-ref'] + ':' + cpRef['member-vnf-index-ref']) {
334               this.links.push({ source: this.nodes[vldPos], target: this.nodes[this.cpCount] });
335             }
336             this.cpCount++;
337           });
338         });
339         vldPos++;
340       }
341     });
342   }
343
344   /** Node is created and render at D3 region @private */
345   private createNode(nodes: NSINSTANCENODES[], links: {}[]): void {
346     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
347     d3.selectAll('svg#graphContainer > *').remove();
348     d3.select(window).on('keydown', () => { this.keyDown(); });
349     d3.select(window).on('keyup', () => { this.keyUp(); });
350     this.svg = d3.select('#graphContainer')
351       .attr('oncontextmenu', 'return false;')
352       .attr('width', graphContainerAttr.width)
353       .attr('height', graphContainerAttr.height);
354     this.force = d3.forceSimulation()
355       .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength))
356       .force('link', d3.forceLink().id((d: TickPath) => d.id).distance(graphContainerAttr.distance))
357       .force('center', d3.forceCenter(graphContainerAttr.width / graphContainerAttr.forcex,
358         graphContainerAttr.height / graphContainerAttr.forcey))
359       .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex))
360       .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey))
361       .on('tick', () => { this.tick(); });
362     // handles to link and node element groups
363     this.path = this.svg.append('svg:g').selectAll('path');
364     this.network = this.svg.append('svg:g').selectAll('network');
365     this.square = this.svg.append('svg:g').selectAll('rect');
366     this.circle = this.svg.append('svg:g').selectAll('circle');
367     this.restart(nodes, links);
368   }
369
370   /** Update force layout (called automatically each iteration) @private */
371   private tick(): void {
372     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
373     // draw directed edges with proper padding from node centers
374     this.path.attr('class', 'link').attr('d', (d: Tick) => {
375       const deltaX: number = d.target.x - d.source.x;
376       const deltaY: number = d.target.y - d.source.y;
377       const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
378       const normX: number = deltaX / dist;
379       const normY: number = deltaY / dist;
380       const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
381       const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
382       const sourceX: number = d.source.x + (sourcePadding * normX);
383       const sourceY: number = d.source.y + (sourcePadding * normY);
384       const targetX: number = d.target.x - (targetPadding * normX);
385       const targetY: number = d.target.y - (targetPadding * normY);
386       return `M${sourceX},${sourceY}L${targetX},${targetY}`;
387     });
388     this.network.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
389     this.square.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
390     this.circle.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`);
391   }
392
393   /** Update graph (called when needed) @private */
394   private restart(nodes: NSINSTANCENODES[], links: {}[]): void {
395     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
396     this.path = this.path.data(links);
397     const vnfdNodes: {}[] = []; const vldNodes: {}[] = []; const cpNodes: {}[] = []; // NB: Nodes are known by id, not by index!
398     nodes.forEach((nodeList: NSINSTANCENODES) => {
399       if (nodeList.nodeTypeRef === 'vnfd') { vnfdNodes.push(nodeList); }
400       else if (nodeList.nodeTypeRef === 'vld') { vldNodes.push(nodeList); }
401       else if (nodeList.nodeTypeRef === 'cp') { cpNodes.push(nodeList); }
402     });
403     this.square = this.square.data(vnfdNodes, (d: { id: number }) => d.id);
404     this.network = this.network.data(vldNodes, (d: { id: number }) => d.id);
405     this.circle = this.circle.data(cpNodes, (d: { id: number }) => d.id);
406     this.resetAndCreateNodes();
407     this.force.nodes(nodes).force('link').links(links); //Set the graph in motion
408     this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
409   }
410
411   /** Rest and create nodes @private */
412   private resetAndCreateNodes(): void {
413     this.path.exit().remove();
414     this.square.exit().remove();
415     this.network.exit().remove();
416     this.circle.exit().remove();
417     // tslint:disable-next-line:no-any
418     const gPath: any = this.path.enter().append('svg:path').attr('class', 'link');
419     this.getgSquare();
420     this.getgNetwork();
421     this.getgCircle();
422     this.square = this.gSquare.merge(this.square);
423     this.network = this.gNetwork.merge(this.network);
424     this.path = gPath.merge(this.path);
425     this.circle = this.gCircle.merge(this.circle);
426   }
427
428   /** Events handles when Shift Click to perform create cp @private */
429   // tslint:disable-next-line: no-any
430   private singleClick(nodeSelected: any, d: COMPOSERNODES): void {
431     this.selectNodeExclusive(nodeSelected, d);
432   }
433   /** Selected nodes @private */
434   // tslint:disable-next-line: no-any
435   private selectNodeExclusive(nodeSelected: any, d: COMPOSERNODES): void {
436     const alreadyIsActive: boolean = nodeSelected.select('#' + d.selectorId).classed(this.activeClass);
437     this.deselectAllNodes();
438     nodeSelected.select('#' + d.selectorId).classed(this.activeClass, !alreadyIsActive);
439     if (d.nodeTypeRef === 'vld' && !alreadyIsActive) {
440       this.virtualLink = {
441         id: d.id,
442         name: d.name,
443         type: d.type,
444         shortName: d.shortName,
445         vimNetworkName: d.vimNetworkName
446       };
447       this.showRightSideInfo(false, true, false);
448     } else if (d.nodeTypeRef === 'vnfd' && !alreadyIsActive) {
449       this.vnfr = {
450         vimID: d.vimID,
451         _id: d.vndfrID,
452         ip: d.ipAddress,
453         nsrID: d.nsID,
454         id: d.selectorId,
455         vnfdRef: d.vnfdRef,
456         vnfdId: d.vnfdID,
457         memberIndex: d.memberIndex
458       };
459       this.showRightSideInfo(false, false, true);
460     } else {
461       this.showRightSideInfo(true, false, false);
462     }
463   }
464   /** Setting all the square/vnf attributes of nodes @private */
465   private getgSquare(): void {
466     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
467     this.gSquare = this.square.enter().append('svg:g');
468     this.gSquare.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
469     this.gSquare.append('svg:image')
470       .style('opacity', 1)
471       .attr('x', graphContainerAttr.imageX)
472       .attr('y', graphContainerAttr.imageY)
473       .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
474       .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
475       .attr('xlink:href', 'assets/images/VNFD.svg')
476       .on('click', (d: COMPOSERNODES) => { this.singleClick(this.gSquare, d); this.onNodeClickToggleSidebar(); });
477     this.gSquare.append('svg:text')
478       .attr('class', 'node_text')
479       .attr('y', graphContainerAttr.textY)
480       .text((d: COMPOSERNODES) => d.id);
481   }
482
483   /** Settings all the network attributes of nodes @private */
484   private getgNetwork(): void {
485     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
486     this.gNetwork = this.network.enter().append('svg:g');
487     this.gNetwork.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
488     this.gNetwork.append('svg:image')
489       .style('opacity', 1)
490       .attr('x', graphContainerAttr.imageX)
491       .attr('y', graphContainerAttr.imageY)
492       .attr('id', (d: COMPOSERNODES) => { return d.selectorId; })
493       .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
494       .attr('xlink:href', 'assets/images/VL.svg')
495       .on('click', (d: COMPOSERNODES) => { this.singleClick(this.gNetwork, d); this.onNodeClickToggleSidebar(); });
496     this.gNetwork.append('svg:text')
497       .attr('class', 'node_text')
498       .attr('y', graphContainerAttr.textY)
499       .text((d: COMPOSERNODES) => d.name);
500   }
501
502   /** Settings all the connection point attributes of nodes @private */
503   private getgCircle(): void {
504     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
505     this.gCircle = this.circle.enter().append('svg:g');
506     this.gCircle.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
507     this.gCircle.append('svg:image')
508       .style('opacity', 1)
509       .attr('x', graphContainerAttr.imageX)
510       .attr('y', graphContainerAttr.imageY)
511       .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
512       .attr('xlink:href', 'assets/images/CP.svg');
513     this.gCircle.append('svg:text')
514       .attr('class', 'node_text')
515       .attr('y', graphContainerAttr.textY)
516       .text((d: COMPOSERNODES) => d.name);
517   }
518
519   /** Key press event @private */
520   private keyDown(): void {
521     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
522     if (this.lastKeyDown !== -1) { return; }
523     this.lastKeyDown = d3.event.keyCode;
524     if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
525       this.gSquare.call(d3.drag()
526         .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
527       );
528       this.gNetwork.call(d3.drag()
529         .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
530       );
531       this.gCircle.call(d3.drag()
532         .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
533       );
534       this.svg.classed('ctrl', true);
535     }
536   }
537   /** Key realse event @private */
538   private keyUp(): void {
539     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
540     this.lastKeyDown = -1;
541     if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
542       this.gSquare.on('.drag', null);
543       this.gNetwork.on('.drag', null);
544       this.gCircle.on('.drag', null);
545       this.svg.classed('ctrl', false);
546     }
547   }
548   /** Events handles when dragstarted @private */
549   private dragstarted(d: COMPOSERNODES): void {
550     d.fx = d.x;
551     d.fy = d.y;
552   }
553   /** Events handles when dragged @private */
554   private dragged(d: COMPOSERNODES): void {
555     d.fx = d.x = d3.event.x;
556     d.fy = d.y = d3.event.y;
557   }
558   /** Events handles when dragended @private */
559   private dragended(d: COMPOSERNODES): void {
560     if (this.forceSimulationActive) {
561       d.fx = null;
562       d.fy = null;
563     } else {
564       d.fx = d.x;
565       d.fy = d.y;
566       this.forceSimulationActive = false;
567     }
568   }
569   /** Events handles when node single click   @private */
570   private onNodeClickToggleSidebar(): void {
571     this.sideBarOpened = true;
572   }
573 }