Bug 1383 Error when consulting NS instance's topology
[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 { CCI, DF, VLC, VNFPROFILE } 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): void => {
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)
215       .subscribe((nsData: NSInstanceDetails): void => {
216         this.nsData = nsData;
217         this.nsInfo = {
218           nsInstanceID: nsData._id,
219           nsName: nsData.name,
220           nsOperationalStatus: nsData['operational-status'],
221           nsConfigStatus: nsData['config-status'],
222           nsDetailedStatus: nsData['detailed-status'],
223           nsResourceOrchestrator: nsData['resource-orchestrator']
224         };
225         if (this.nsData['constituent-vnfr-ref'] !== undefined) {
226           this.generateVNFRCPNodes();
227         }
228         if (this.nsData.vld !== undefined) {
229           this.generateVLDNetworkNodes();
230         }
231         setTimeout((): void => {
232           this.pushAllNodes();
233           this.generateVNFDCP();
234           this.generateVLDCP();
235           this.isLoadingResults = false;
236           this.createNode(this.nodes, this.links);
237         }, this.TIMEOUT);
238       }, (error: ERRORDATA): void => {
239         this.isLoadingResults = false;
240         if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
241           this.router.navigateByUrl('404', { skipLocationChange: true }).catch();
242         } else {
243           this.restService.handleError(error, 'get');
244         }
245       });
246   }
247   /** Fetching all the VNFR Information @private */
248   private generateVNFRCPNodes(): void {
249     this.nsData['constituent-vnfr-ref'].forEach((vnfdrID: string): void => {
250       this.restService.getResource(environment.VNFINSTANCES_URL + '/' + vnfdrID).subscribe((vndfrDetail: NSD): void => {
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           nsID: vndfrDetail['nsr-id-ref'],
257           vnfdID: vndfrDetail['vnfd-id'],
258           vimID: vndfrDetail['vim-account-id'],
259           vndfrID: vndfrDetail.id,
260           ipAddress: vndfrDetail['ip-address'],
261           memberIndex: vndfrDetail['member-vnf-index-ref'],
262           vnfdRef: vndfrDetail['vnfd-ref'],
263           selectorId: 'nsInst-' + vndfrDetail.id
264         });
265         // Fetching all the connection point of VNF & Interface
266         vndfrDetail['connection-point'].forEach((cp: CONNECTIONPOINT): void => {
267           this.nodes.push({
268             id: cp.name + ':' + vndfrDetail['member-vnf-index-ref'],
269             vndfCPRef: vndfrDetail['vnfd-ref'] + ':' + vndfrDetail['member-vnf-index-ref'],
270             nodeTypeRef: 'cp',
271             name: cp.name
272           });
273         });
274       }, (error: ERRORDATA): void => {
275         this.restService.handleError(error, 'get');
276       });
277     });
278   }
279   /** Fetching all the VLD/Network Information @private */
280   private generateVLDNetworkNodes(): void {
281     this.nsdData = this.nsData.nsd;
282     this.nsdData['virtual-link-desc'].forEach((ref: NSDVLD): void => {
283       this.nodes.push({
284         id: ref.id,
285         nodeTypeRef: 'vld',
286         name: ref.id,
287         type: ref.type,
288         vnfdCP: this.nsdData.df,
289         vimNetworkName: ref['vim-network-name'],
290         selectorId: 'nsInst-' + ref.id
291       });
292     });
293   }
294   /** Pushing connection points of path/links nodes @private */
295   private pushAllNodes(): void {
296     this.nodes.forEach((nodeList: NSINSTANCENODES): void => {
297       if (nodeList.nodeTypeRef === 'vnfd') {
298         this.vnfdNodes.push(nodeList);
299       } else if (nodeList.nodeTypeRef === 'vld') {
300         this.vldNodes.push(nodeList);
301       } else if (nodeList.nodeTypeRef === 'cp') {
302         this.cpNodes.push(nodeList);
303       }
304     });
305   }
306   /** Get CP position based on vndf @private */
307   private generateVNFDCP(): void {
308     this.vnfdNodes.forEach((list: NSINSTANCENODES): void => {
309       const vndfPos: number = this.nodes.map((e: NSINSTANCENODES): string => { return e.id; }).indexOf(list.id);
310       this.cpCount = 0;
311       this.nodes.forEach((res: NSINSTANCENODES): void => {
312         if (res.nodeTypeRef === 'cp' && res.vndfCPRef === list.id) {
313           this.links.push({ source: this.nodes[vndfPos], target: this.nodes[this.cpCount] });
314         }
315         this.cpCount++;
316       });
317     });
318   }
319   /** Get CP position based on vld @private */
320   private generateVLDCP(): void {
321     let vldPos: number = 0;
322     this.vldNodes.forEach((list: NSINSTANCENODES): void => {
323       if (!isNullOrUndefined(list.vnfdCP)) {
324         list.vnfdCP.forEach((cpRef: DF): void => {
325           cpRef['vnf-profile'].forEach((vnfProfile: VNFPROFILE): void => {
326             vnfProfile['virtual-link-connectivity'].forEach((resultVLC: VLC, index: number): void => {
327               resultVLC['constituent-cpd-id'].forEach((resultCCI: CCI): void => {
328                 this.cpCount = 0;
329                 this.nodes.forEach((res: NSINSTANCENODES): void => {
330                   if (res.nodeTypeRef === 'cp' &&
331                     res.id === resultCCI['constituent-cpd-id'] + ':' + resultCCI['constituent-base-element-id']) {
332                     this.links.push({ source: this.nodes[vldPos], target: this.nodes[this.cpCount] });
333                   }
334                   this.cpCount++;
335                 });
336               });
337             });
338           });
339         });
340         vldPos++;
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', (): void => { this.keyDown(); });
349     d3.select(window).on('keyup', (): void => { 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): string => 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', (): void => { 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   /** Update force layout (called automatically each iteration) @private */
370   private tick(): void {
371     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
372     // draw directed edges with proper padding from node centers
373     this.path.attr('class', 'link').attr('d', (d: Tick): string => {
374       const deltaX: number = d.target.x - d.source.x;
375       const deltaY: number = d.target.y - d.source.y;
376       const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
377       const normX: number = deltaX / dist;
378       const normY: number = deltaY / dist;
379       const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
380       const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
381       const sourceX: number = d.source.x + (sourcePadding * normX);
382       const sourceY: number = d.source.y + (sourcePadding * normY);
383       const targetX: number = d.target.x - (targetPadding * normX);
384       const targetY: number = d.target.y - (targetPadding * normY);
385       return `M${sourceX},${sourceY}L${targetX},${targetY}`;
386     });
387     this.network.attr('transform', (t: TickPath): string => `translate(${t.x},${t.y})`);
388     this.square.attr('transform', (t: TickPath): string => `translate(${t.x},${t.y})`);
389     this.circle.attr('transform', (t: TickPath): string => `translate(${t.x},${t.y})`);
390   }
391   /** Update graph (called when needed) @private */
392   private restart(nodes: NSINSTANCENODES[], links: {}[]): void {
393     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
394     this.path = this.path.data(links);
395     const vnfdNodes: {}[] = []; const vldNodes: {}[] = []; const cpNodes: {}[] = []; // NB: Nodes are known by id, not by index!
396     nodes.forEach((nodeList: NSINSTANCENODES): void => {
397       if (nodeList.nodeTypeRef === 'vnfd') { vnfdNodes.push(nodeList); }
398       else if (nodeList.nodeTypeRef === 'vld') { vldNodes.push(nodeList); }
399       else if (nodeList.nodeTypeRef === 'cp') { cpNodes.push(nodeList); }
400     });
401     this.square = this.square.data(vnfdNodes, (d: COMPOSERNODES): string => d.id);
402     this.network = this.network.data(vldNodes, (d: COMPOSERNODES): string => d.id);
403     this.circle = this.circle.data(cpNodes, (d: COMPOSERNODES): string => d.id);
404     this.resetAndCreateNodes();
405     this.force.nodes(nodes).force('link').links(links); //Set the graph in motion
406     this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
407   }
408   /** Rest and create nodes @private */
409   private resetAndCreateNodes(): void {
410     this.path.exit().remove();
411     this.square.exit().remove();
412     this.network.exit().remove();
413     this.circle.exit().remove();
414     // tslint:disable-next-line:no-any
415     const gPath: any = this.path.enter().append('svg:path').attr('class', 'link');
416     this.getgSquare();
417     this.getgNetwork();
418     this.getgCircle();
419     this.square = this.gSquare.merge(this.square);
420     this.network = this.gNetwork.merge(this.network);
421     this.path = gPath.merge(this.path);
422     this.circle = this.gCircle.merge(this.circle);
423   }
424   /** Events handles when Shift Click to perform create cp @private */
425   // tslint:disable-next-line: no-any
426   private singleClick(nodeSelected: any, d: COMPOSERNODES): void {
427     this.selectNodeExclusive(nodeSelected, d);
428   }
429   /** Selected nodes @private */
430   // tslint:disable-next-line: no-any
431   private selectNodeExclusive(nodeSelected: any, d: COMPOSERNODES): void {
432     const alreadyIsActive: boolean = nodeSelected.select('#' + d.selectorId).classed(this.activeClass);
433     this.deselectAllNodes();
434     nodeSelected.select('#' + d.selectorId).classed(this.activeClass, !alreadyIsActive);
435     if (d.nodeTypeRef === 'vld' && !alreadyIsActive) {
436       this.virtualLink = {
437         id: d.id,
438         name: d.name,
439         type: d.type,
440         shortName: d.shortName,
441         vimNetworkName: d.vimNetworkName
442       };
443       this.showRightSideInfo(false, true, false);
444     } else if (d.nodeTypeRef === 'vnfd' && !alreadyIsActive) {
445       this.vnfr = {
446         vimID: d.vimID,
447         _id: d.vndfrID,
448         ip: d.ipAddress,
449         nsrID: d.nsID,
450         id: d.selectorId,
451         vnfdRef: d.vnfdRef,
452         vnfdId: d.vnfdID,
453         memberIndex: d.memberIndex
454       };
455       this.showRightSideInfo(false, false, true);
456     } else {
457       this.showRightSideInfo(true, false, false);
458     }
459   }
460   /** Setting all the square/vnf attributes of nodes @private */
461   private getgSquare(): void {
462     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
463     this.gSquare = this.square.enter().append('svg:g');
464     this.gSquare.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
465     this.gSquare.append('svg:image')
466       .style('opacity', 1)
467       .attr('x', graphContainerAttr.imageX)
468       .attr('y', graphContainerAttr.imageY)
469       .call(this.onDragDrop())
470       .attr('id', (d: COMPOSERNODES): string => { return d.selectorId; })
471       .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
472       .attr('xlink:href', 'assets/images/VNFD.svg')
473       .on('click', (d: COMPOSERNODES): void => { this.singleClick(this.gSquare, d); this.onNodeClickToggleSidebar(); });
474     this.gSquare.append('svg:text')
475       .attr('class', 'node_text')
476       .attr('y', graphContainerAttr.textY)
477       .text((d: COMPOSERNODES): string => d.id);
478   }
479   /** Settings all the network attributes of nodes @private */
480   private getgNetwork(): void {
481     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
482     this.gNetwork = this.network.enter().append('svg:g');
483     this.gNetwork.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
484     this.gNetwork.append('svg:image')
485       .style('opacity', 1)
486       .attr('x', graphContainerAttr.imageX)
487       .attr('y', graphContainerAttr.imageY)
488       .call(this.onDragDrop())
489       .attr('id', (d: COMPOSERNODES): string => { return d.selectorId; })
490       .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
491       .attr('xlink:href', 'assets/images/VL.svg')
492       .on('click', (d: COMPOSERNODES): void => { this.singleClick(this.gNetwork, d); this.onNodeClickToggleSidebar(); });
493     this.gNetwork.append('svg:text')
494       .attr('class', 'node_text')
495       .attr('y', graphContainerAttr.textY)
496       .text((d: COMPOSERNODES): string => d.name);
497   }
498   /** Settings all the connection point attributes of nodes @private */
499   private getgCircle(): void {
500     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
501     this.gCircle = this.circle.enter().append('svg:g');
502     this.gCircle.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
503     this.gCircle.append('svg:image')
504       .style('opacity', 1)
505       .attr('x', graphContainerAttr.imageX)
506       .attr('y', graphContainerAttr.imageY)
507       .call(this.onDragDrop())
508       .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
509       .attr('xlink:href', 'assets/images/CP.svg');
510     this.gCircle.append('svg:text')
511       .attr('class', 'node_text')
512       .attr('y', graphContainerAttr.textY)
513       .text((d: COMPOSERNODES): string => d.name);
514   }
515   /** drag event @private */
516   // tslint:disable-next-line: no-any
517   private onDragDrop(): any {
518     return d3.drag().filter(this.dragFilter)
519       .on('start', this.dragstarted)
520       .on('drag', this.dragged)
521       .on('end', this.dragended);
522   }
523   /** Key press event @private */
524   private keyDown(): void {
525     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
526     if (this.lastKeyDown !== -1) { return; }
527     this.lastKeyDown = d3.event.keyCode;
528     if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
529       this.gSquare.call(d3.drag()
530         .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
531       );
532       this.gNetwork.call(d3.drag()
533         .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
534       );
535       this.gCircle.call(d3.drag()
536         .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)
537       );
538       this.svg.classed('ctrl', true);
539     }
540   }
541   /** Key realse event @private */
542   private keyUp(): void {
543     const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
544     this.lastKeyDown = -1;
545     if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
546       this.gSquare.on('.drag', null);
547       this.gNetwork.on('.drag', null);
548       this.gCircle.on('.drag', null);
549       this.svg.classed('ctrl', false);
550     }
551   }
552   /** Events handles when to drag using filter for the keys @private */
553   private dragFilter(): boolean {
554     return d3.event.ctrlKey && !d3.event.button;
555   }
556   /** Events handles when dragstarted @private */
557   private dragstarted(d: COMPOSERNODES): void {
558     d.fx = d.x;
559     d.fy = d.y;
560   }
561   /** Events handles when dragged @private */
562   private dragged(d: COMPOSERNODES): void {
563     d.fx = d.x = d3.event.x;
564     d.fy = d.y = d3.event.y;
565   }
566   /** Events handles when dragended @private */
567   private dragended(d: COMPOSERNODES): void {
568     if (this.forceSimulationActive) {
569       d.fx = null;
570       d.fy = null;
571     } else {
572       d.fx = d.x;
573       d.fy = d.y;
574       this.forceSimulationActive = false;
575     }
576   }
577   /** Events handles when node single click   @private */
578   private onNodeClickToggleSidebar(): void {
579     this.sideBarOpened = true;
580   }
581 }