Keyboard Shortcut for copy&Paste and delete
[sdc.git] / catalog-ui / src / app / directives / graphs-v2 / composition-graph / composition-graph.directive.ts
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 import * as _ from "lodash";
22 import {
23     Match,
24     LinkMenu,
25     ComponentInstance,
26     LeftPaletteComponent,
27     Relationship,
28     Component,
29     Service,
30     ConnectRelationModel,
31     CompositionCiNodeBase,
32     CompositionCiNodeVl,
33     ModalModel,
34     ButtonModel,
35     NodesFactory,
36     Point
37 } from "app/models";
38 import { ComponentInstanceFactory, ComponentFactory, GRAPH_EVENTS, GraphColors, GraphUIObjects, ModalsHandler } from "app/utils";
39 import { EventListenerService, LoaderService } from "app/services";
40 import { CompositionGraphLinkUtils } from "./utils/composition-graph-links-utils";
41 import { CompositionGraphGeneralUtils } from "./utils/composition-graph-general-utils";
42 import { CompositionGraphNodesUtils } from "./utils/composition-graph-nodes-utils";
43 import { CommonGraphUtils } from "../common/common-graph-utils";
44 import { MatchCapabilitiesRequirementsUtils } from "./utils/match-capability-requierment-utils";
45 import { CompositionGraphPaletteUtils } from "./utils/composition-graph-palette-utils";
46 import { ComponentInstanceNodesStyle } from "../common/style/component-instances-nodes-style";
47 import { CytoscapeEdgeEditation } from 'third-party/cytoscape.js-edge-editation/CytoscapeEdgeEditation.js';
48 import { ComponentServiceNg2 } from "../../../ng2/services/component-services/component.service";
49 import { ComponentGenericResponse } from "../../../ng2/services/responses/component-generic-response";
50 import { ModalService } from "../../../ng2/services/modal.service";
51 import { ConnectionWizardService } from "../../../ng2/pages/connection-wizard/connection-wizard.service";
52 import { StepModel } from "../../../models/wizard-step";
53 import { FromNodeStepComponent } from "app/ng2/pages/connection-wizard/from-node-step/from-node-step.component";
54 import { PropertiesStepComponent } from "app/ng2/pages/connection-wizard/properties-step/properties-step.component";
55 import { ToNodeStepComponent } from "app/ng2/pages/connection-wizard/to-node-step/to-node-step.component";
56 import { ConnectionWizardHeaderComponent } from "app/ng2/pages/connection-wizard/connection-wizard-header/connection-wizard-header.component";
57 import { ConnectionPropertiesViewComponent } from "../../../ng2/pages/connection-wizard/connection-properties-view/connection-properties-view.component";
58 import { ComponentInstanceServiceNg2 } from "../../../ng2/services/component-instance-services/component-instance.service";
59 import { EVENTS } from "../../../utils/constants";
60 import { PropertyBEModel } from "../../../models/properties-inputs/property-be-model";
61 import { ForwardingPath } from "app/models/forwarding-path";
62 import { ServicePathGraphUtils } from "./utils/composition-graph-service-path-utils";
63 import { CompositionCiServicePathLink } from "app/models/graph/graph-links/composition-graph-links/composition-ci-service-path-link";
64 import {
65     ZoneInstance, ZoneInstanceMode, ZoneInstanceType,
66     ZoneInstanceAssignmentType
67 } from "app/models/graph/zones/zone-instance";
68
69 import { Zone } from "app/models/graph/zones/zone";
70 import { CompositionGraphZoneUtils } from "./utils/composition-graph-zone-utils";
71 import { UIZoneInstanceObject } from "../../../models/ui-models/ui-zone-instance-object";
72 import { GroupInstance } from "../../../models/graph/zones/group-instance";
73 import { PolicyInstance } from "../../../models/graph/zones/policy-instance";
74
75
76 export interface ICompositionGraphScope extends ng.IScope {
77
78     component: Component;
79     isLoading: boolean;
80     isViewOnly: boolean;
81     withSidebar: boolean;
82
83     //zones
84     newZoneInstance;
85     zoneTagMode: string;
86     activeZoneInstance: ZoneInstance;
87     zones: Array<Zone>;
88     zoneMinimizeToggle(zoneType: ZoneInstanceType): void;
89     zoneInstanceTagged(taggedInstance: ZoneInstance): void;
90     zoneInstanceModeChanged(newMode: ZoneInstanceMode, instance: ZoneInstance, zoneId: ZoneInstanceType);
91     unsetActiveZoneInstance(): void;
92     clickOutsideZoneInstance(): void;
93     zoneAssignmentSaveStart(): void;
94     zoneAssignmentSaveComplete(success: boolean): void;
95
96     // Link menu - create link menu
97     relationMenuDirectiveObj: ConnectRelationModel;
98     isLinkMenuOpen: boolean;
99     createLinkFromMenu: (chosenMatch: Match, vl: Component) => void;
100     saveChangedCapabilityProperties: () => Promise<PropertyBEModel[]>;
101
102     //modify link menu - for now only delete menu
103     relationMenuTimeout: ng.IPromise<any>;
104     linkMenuObject: LinkMenu;
105     isOnDrag: boolean;
106
107     //left palette functions callbacks
108     dropCallback(event: JQueryEventObject, ui: any): void;
109     beforeDropCallback(event: IDragDropEvent): void;
110     verifyDrop(event: JQueryEventObject, ui: any): void;
111
112     //Links menus
113     viewRelation(link: Cy.CollectionEdges): void;
114     deleteRelation(link: Cy.CollectionEdges): void;
115     hideRelationMenu();
116
117     //search,zoom in/out/all
118     componentInstanceNames: Array<string>; //id, name
119     zoom(zoomIn: boolean): void;
120     zoomAllWithoutSidebar(): void;
121     getAutoCompleteValues(searchTerm: string): void;
122     highlightSearchMatches(searchTerm: string): void;
123
124     canvasMenuProps: any;
125
126     createOrUpdateServicePath(data: any): void;
127     deletePathsOnCy(): void;
128     drawPathOnCy(data: ForwardingPath): void;
129     selectedPathId: string;
130
131     copyComponentInstance(): void;
132     pasteComponentInstance(event: IDragDropEvent): void;
133
134     origComponentId: string;
135 }
136
137 export class CompositionGraph implements ng.IDirective {
138     private _cy: Cy.Instance;
139     private _currentlyCLickedNodePosition: Cy.Position;
140     private dragElement: JQuery;
141     private dragComponent: ComponentInstance;
142     private cyBackgroundClickEvent: any;
143
144     constructor(private $q: ng.IQService,
145         private $log: ng.ILogService,
146         private $timeout: ng.ITimeoutService,
147         private NodesFactory: NodesFactory,
148         private CompositionGraphLinkUtils: CompositionGraphLinkUtils,
149         private GeneralGraphUtils: CompositionGraphGeneralUtils,
150         private ComponentInstanceFactory: ComponentInstanceFactory,
151         private NodesGraphUtils: CompositionGraphNodesUtils,
152         private eventListenerService: EventListenerService,
153         private ComponentFactory: ComponentFactory,
154         private LoaderService: LoaderService,
155         private commonGraphUtils: CommonGraphUtils,
156         private matchCapabilitiesRequirementsUtils: MatchCapabilitiesRequirementsUtils,
157         private CompositionGraphPaletteUtils: CompositionGraphPaletteUtils,
158         private compositionGraphZoneUtils: CompositionGraphZoneUtils,
159         private ComponentServiceNg2: ComponentServiceNg2,
160         private ModalServiceNg2: ModalService,
161         private ConnectionWizardServiceNg2: ConnectionWizardService,
162         private ComponentInstanceServiceNg2: ComponentInstanceServiceNg2,
163         private servicePathGraphUtils: ServicePathGraphUtils,
164         private ModalsHandler: ModalsHandler) {
165
166     }
167
168     restrict = 'E';
169     template = require('./composition-graph.html');
170     scope = {
171         component: '=',
172         isViewOnly: '=',
173         withSidebar: '='
174     };
175
176     link = (scope: ICompositionGraphScope, el: JQuery) => {
177         this.loadGraph(scope, el);
178
179         if (!scope.component.groupInstances || !scope.component.policies) {
180             this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED, () => {
181                 this.loadGraphData(scope);
182             });
183         } else {
184             this.loadGraphData(scope);
185         }
186
187
188         scope.$on('$destroy', () => {
189             this._cy.destroy();
190             _.forEach(GRAPH_EVENTS, (event) => {
191                 this.eventListenerService.unRegisterObserver(event);
192             });
193             this.eventListenerService.unRegisterObserver(EVENTS.SHOW_LOADER_EVENT + 'composition-graph');
194             this.eventListenerService.unRegisterObserver(EVENTS.HIDE_LOADER_EVENT + 'composition-graph');
195         });
196
197     };
198
199     private loadGraphData = (scope: ICompositionGraphScope) => {
200         this.initGraphNodes(scope.component.componentInstances, scope.isViewOnly);
201         this.commonGraphUtils.initGraphLinks(this._cy, scope.component.componentInstancesRelations, scope.component.getRelationRequirementCapability.bind(scope.component));
202         this.commonGraphUtils.initUcpeChildren(this._cy);
203         this.compositionGraphZoneUtils.initZoneInstances(scope.zones, scope.component);
204         setTimeout(() => {//Need settimeout so that angular canvas changes will take effect before resize & center
205             this.GeneralGraphUtils.zoomAllWithMax(this._cy, 1);
206         });
207     }
208
209     private loadGraph = (scope: ICompositionGraphScope, el: JQuery) => {
210         let graphEl = el.find('.sdc-composition-graph-wrapper');
211         this.initGraph(graphEl, scope.isViewOnly);
212         this.initDropZone(scope);
213         this.initZones(scope);
214         this.registerCytoscapeGraphEvents(scope);
215         this.registerCustomEvents(scope, el);
216         this.initViewMode(scope.isViewOnly);
217     };
218
219     private initGraph(graphEl: JQuery, isViewOnly: boolean) {
220
221         this._cy = cytoscape({
222             container: graphEl,
223             style: ComponentInstanceNodesStyle.getCompositionGraphStyle(),
224             zoomingEnabled: true,
225             maxZoom: 1.2,
226             minZoom: .1,
227             userZoomingEnabled: false,
228             userPanningEnabled: true,
229             selectionType: 'single',
230             boxSelectionEnabled: true,
231             autolock: isViewOnly,
232             autoungrabify: isViewOnly
233         });
234     }
235
236     private initViewMode(isViewOnly: boolean) {
237
238         if (isViewOnly) {
239             //remove event listeners
240             this._cy.off('drag');
241             this._cy.off('handlemouseout');
242             this._cy.off('handlemouseover');
243             this._cy.off('canvasredraw');
244             this._cy.off('handletagclick')
245             this._cy.edges().unselectify();
246         }
247     };
248
249     private registerCustomEvents(scope: ICompositionGraphScope, el: JQuery) {
250
251         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_GROUP_INSTANCE_UPDATE, (groupInstance: GroupInstance) => {
252             this.compositionGraphZoneUtils.findAndUpdateZoneInstanceData(scope.zones, groupInstance);
253             this.GeneralGraphUtils.showGroupUpdateSuccess();
254         });
255
256         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_POLICY_INSTANCE_UPDATE, (policyInstance: PolicyInstance) => {
257             this.compositionGraphZoneUtils.findAndUpdateZoneInstanceData(scope.zones, policyInstance);
258             this.GeneralGraphUtils.showPolicyUpdateSuccess();
259         });
260
261         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_IN, (leftPaletteComponent: LeftPaletteComponent) => {
262             if (scope.isOnDrag) {
263                 return;
264             }
265
266             this.$log.info(`composition-graph::registerEventServiceEvents:: palette hover on component: ${leftPaletteComponent.uniqueId}`);
267
268             let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes());
269             let nodesLinks = this.GeneralGraphUtils.getAllCompositionCiLinks(this._cy);
270
271             if (this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.containsKey(leftPaletteComponent.uniqueId)) {
272                 let cacheComponent = this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.getValue(leftPaletteComponent.uniqueId);
273                 let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findMatchingNodes(cacheComponent, nodesData, nodesLinks);
274
275                 this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
276                 this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy);
277
278                 return;
279             }
280
281             //----------------------- ORIT TO FIX------------------------//
282
283             this.ComponentServiceNg2.getCapabilitiesAndRequirements(leftPaletteComponent.componentType, leftPaletteComponent.uniqueId).subscribe((response: ComponentGenericResponse) => {
284
285                 let component = this.ComponentFactory.createEmptyComponent(leftPaletteComponent.componentType);
286                 component.uniqueId = component.uniqueId;
287                 component.capabilities = response.capabilities;
288                 component.requirements = response.requirements;
289                 this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.setValue(leftPaletteComponent.uniqueId, component);
290             });
291         });
292
293         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ADD_ZONE_INSTANCE_FROM_PALETTE, (component: Component, paletteComponent: LeftPaletteComponent, startPosition: Point) => {
294
295             let zoneType: ZoneInstanceType = this.compositionGraphZoneUtils.getZoneTypeForPaletteComponent(paletteComponent.categoryType);
296             this.compositionGraphZoneUtils.showZone(scope.zones[zoneType]);
297
298             this.LoaderService.showLoader('composition-graph');
299             this.compositionGraphZoneUtils.createZoneInstanceFromLeftPalette(zoneType, component, paletteComponent.type).subscribe((zoneInstance: ZoneInstance) => {
300                 this.LoaderService.hideLoader('composition-graph');
301                 this.compositionGraphZoneUtils.addInstanceToZone(scope.zones[zoneInstance.type], zoneInstance, true);
302                 this.compositionGraphZoneUtils.createPaletteToZoneAnimation(startPosition, zoneType, zoneInstance);
303             }, (error) => {
304                 this.LoaderService.hideLoader('composition-graph');
305             });
306         });
307
308         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_OUT, () => {
309
310             this._cy.emit('hidehandles');
311             this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
312         });
313
314         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_START, (dragElement, dragComponent) => {
315
316             this.dragElement = dragElement;
317             this.dragComponent = this.ComponentInstanceFactory.createComponentInstanceFromComponent(dragComponent);
318         });
319
320         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_ACTION, (event: IDragDropEvent) => {
321             this.CompositionGraphPaletteUtils.onComponentDrag(this._cy, event, this.dragElement, this.dragComponent);
322
323         });
324
325         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, (component: ComponentInstance) => {
326
327             let selectedNode = this._cy.getElementById(component.uniqueId);
328             selectedNode.data().componentInstance.name = component.name;
329             selectedNode.data('name', component.name); //used for tooltip
330             selectedNode.data('displayName', selectedNode.data().getDisplayName()); //abbreviated
331
332         });
333
334         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, (componentInstance: ComponentInstance) => {
335             let nodeToDelete = this._cy.getElementById(componentInstance.uniqueId);
336             this.NodesGraphUtils.deleteNode(this._cy, scope.component, nodeToDelete);
337         });
338
339         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_ZONE_INSTANCE, (deletedInstance: UIZoneInstanceObject) => {
340
341             if (deletedInstance.type === ZoneInstanceType.POLICY) {
342                 scope.component.policies = scope.component.policies.filter(policy => policy.uniqueId !== deletedInstance.uniqueId);
343             } else if (deletedInstance.type === ZoneInstanceType.GROUP) {
344                 scope.component.groupInstances = scope.component.groupInstances.filter(group => group.uniqueId !== deletedInstance.uniqueId);
345             }
346             //remove it from zones
347             scope.zones[deletedInstance.type].removeInstance(deletedInstance.uniqueId);
348             if (deletedInstance.type === ZoneInstanceType.GROUP && !_.isEmpty(scope.zones[ZoneInstanceType.POLICY])) {
349                 this.compositionGraphZoneUtils.updateTargetsOrMembersOnCanvasDelete(deletedInstance.uniqueId, [scope.zones[ZoneInstanceType.POLICY]], ZoneInstanceAssignmentType.GROUPS);
350             }
351             this.eventListenerService.notifyObservers(EVENTS.UPDATE_PANEL);
352         });
353
354         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE_SUCCESS, (componentInstanceId: string) => {
355             if (!_.isEmpty(scope.zones)) {
356                 this.compositionGraphZoneUtils.updateTargetsOrMembersOnCanvasDelete(componentInstanceId, scope.zones, ZoneInstanceAssignmentType.COMPONENT_INSTANCES);
357             }
358         });
359
360         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_EDGE, (releaseLoading: boolean, linksToDelete: Cy.CollectionEdges) => {
361             this.CompositionGraphLinkUtils.deleteLink(this._cy, scope.component, releaseLoading, linksToDelete);
362         });
363
364         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_INSERT_NODE_TO_UCPE, (node: Cy.CollectionNodes, ucpe: Cy.CollectionNodes, updateExistingNode: boolean) => {
365
366             this.commonGraphUtils.initUcpeChildData(node, ucpe);
367             //check if item is a VL, and if so, skip adding the binding to ucpe
368             if (!(node.data() instanceof CompositionCiNodeVl)) {
369                 this.CompositionGraphLinkUtils.createVfToUcpeLink(scope.component, this._cy, ucpe.data(), node.data()); //create link from the node to the ucpe
370             }
371
372             if (updateExistingNode) {
373                 let vlsPendingDeletion: Cy.CollectionNodes = this.NodesGraphUtils.deleteNodeVLsUponMoveToOrFromUCPE(scope.component, node.cy(), node); //delete connected VLs that no longer have 2 links
374                 this.CompositionGraphLinkUtils.deleteLinksWhenNodeMovedFromOrToUCPE(scope.component, node.cy(), node, vlsPendingDeletion); //delete all connected links if needed
375                 this.GeneralGraphUtils.pushUpdateComponentInstanceActionToQueue(scope.component, true, node.data().componentInstance); //update componentInstance position
376             }
377
378         });
379
380         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_REMOVE_NODE_FROM_UCPE, (node: Cy.CollectionNodes, ucpe: Cy.CollectionNodes) => {
381             this.commonGraphUtils.removeUcpeChildData(node);
382             let vlsPendingDeletion: Cy.CollectionNodes = this.NodesGraphUtils.deleteNodeVLsUponMoveToOrFromUCPE(scope.component, node.cy(), node);
383             this.CompositionGraphLinkUtils.deleteLinksWhenNodeMovedFromOrToUCPE(scope.component, node.cy(), node, vlsPendingDeletion); //delete all connected links if needed
384             this.GeneralGraphUtils.pushUpdateComponentInstanceActionToQueue(scope.component, true, node.data().componentInstance); //update componentInstance position
385         });
386
387         this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_VERSION_CHANGED, (component: Component) => {
388             scope.component = component;
389             this._cy.elements().remove();
390             this.loadGraphData(scope);
391         });
392
393
394         scope.zoom = (zoomIn: boolean): void => {
395             let currentZoom: number = this._cy.zoom();
396             if (zoomIn) {
397                 this.GeneralGraphUtils.zoomGraphTo(this._cy, currentZoom + .1);
398             } else {
399                 this.GeneralGraphUtils.zoomGraphTo(this._cy, currentZoom - .1);
400             }
401         }
402
403
404         scope.zoomAllWithoutSidebar = () => {
405             scope.withSidebar = false;
406             setTimeout(() => { //wait for sidebar changes to take effect before zooming
407                 this.GeneralGraphUtils.zoomAll(this._cy);
408             });
409         };
410
411         scope.getAutoCompleteValues = (searchTerm: string) => {
412             if (searchTerm.length > 1) { //US requirement: only display search results after 2nd letter typed.
413                 let nodes: Cy.CollectionNodes = this.NodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
414                 scope.componentInstanceNames = _.map(nodes, node => node.data('name'));
415             } else {
416                 scope.componentInstanceNames = [];
417             }
418         };
419
420         scope.highlightSearchMatches = (searchTerm: string) => {
421             this.NodesGraphUtils.highlightMatchingNodesByName(this._cy, searchTerm);
422             let matchingNodes: Cy.CollectionNodes = this.NodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
423             this.GeneralGraphUtils.zoomAll(this._cy, matchingNodes);
424         };
425
426         scope.saveChangedCapabilityProperties = (): Promise<PropertyBEModel[]> => {
427             return new Promise<PropertyBEModel[]>((resolve) => {
428                 const capabilityPropertiesBE: PropertyBEModel[] = this.ConnectionWizardServiceNg2.changedCapabilityProperties.map((prop) => {
429                     prop.value = prop.getJSONValue();
430                     const propBE = new PropertyBEModel(prop);
431                     propBE.parentUniqueId = this.ConnectionWizardServiceNg2.selectedMatch.relationship.relation.capabilityOwnerId;
432                     return propBE;
433                 });
434                 if (capabilityPropertiesBE.length > 0) {
435                     // if there are capability properties to update, then first update capability properties and then resolve promise
436                     this.ComponentInstanceServiceNg2
437                         .updateInstanceCapabilityProperties(
438                             scope.component,
439                             this.ConnectionWizardServiceNg2.selectedMatch.toNode,
440                             this.ConnectionWizardServiceNg2.selectedMatch.capability,
441                             capabilityPropertiesBE
442                         )
443                         .subscribe((response) => {
444                             console.log("Update resource instance capability properties response: ", response);
445                             this.ConnectionWizardServiceNg2.changedCapabilityProperties = [];
446                             resolve(capabilityPropertiesBE);
447                         });
448                 } else {
449                     // no capability properties to update, immediately resolve promise
450                     resolve(capabilityPropertiesBE);
451                 }
452             });
453         };
454
455         scope.createLinkFromMenu = (): void => {
456             scope.isLinkMenuOpen = false;
457
458             scope.saveChangedCapabilityProperties().then(() => {
459                 //create link:
460                 this.CompositionGraphLinkUtils
461                     .createLinkFromMenu(this._cy, this.ConnectionWizardServiceNg2.selectedMatch, scope.component);
462             });
463         };
464
465         scope.hideRelationMenu = () => {
466             this.commonGraphUtils.safeApply(scope, () => {
467                 delete scope.canvasMenuProps;
468                 this.$timeout.cancel(scope.relationMenuTimeout);
469             });
470         };
471
472         scope.createOrUpdateServicePath = (data: any) => {
473             this.servicePathGraphUtils.createOrUpdateServicePath(scope, data);
474         };
475         scope.deletePathsOnCy = () => {
476             this.servicePathGraphUtils.deletePathsFromGraph(this._cy, <Service>scope.component);
477         };
478         scope.drawPathOnCy = (data: ForwardingPath) => {
479             this.servicePathGraphUtils.drawPath(this._cy, data, <Service>scope.component);
480         };
481
482         scope.viewRelation = (link: Cy.CollectionEdges) => {
483             scope.hideRelationMenu();
484
485             const linkData = link.data();
486             const sourceNode: CompositionCiNodeBase = link.source().data();
487             const targetNode: CompositionCiNodeBase = link.target().data();
488             const relationship: Relationship = linkData.relation.relationships[0];
489
490             scope.component.getRelationRequirementCapability(relationship, sourceNode.componentInstance, targetNode.componentInstance).then((objReqCap) => {
491                 const capability = objReqCap.capability;
492                 const requirement = objReqCap.requirement;
493
494                 this.ConnectionWizardServiceNg2.currentComponent = scope.component;
495                 this.ConnectionWizardServiceNg2.connectRelationModel = new ConnectRelationModel(sourceNode, targetNode, []);
496                 this.ConnectionWizardServiceNg2.selectedMatch = new Match(requirement, capability, true, linkData.source, linkData.target);
497                 this.ConnectionWizardServiceNg2.selectedMatch.relationship = relationship;
498
499                 const title = `Connection Properties`;
500                 const saveButton: ButtonModel = new ButtonModel('Save', 'blue', () => {
501                     scope.saveChangedCapabilityProperties().then(() => {
502                         this.ModalServiceNg2.closeCurrentModal();
503                     })
504                 });
505                 const cancelButton: ButtonModel = new ButtonModel('Cancel', 'white', () => {
506                     this.ModalServiceNg2.closeCurrentModal();
507                 });
508                 const modal = new ModalModel('xl', title, '', [saveButton, cancelButton]);
509                 const modalInstance = this.ModalServiceNg2.createCustomModal(modal);
510                 this.ModalServiceNg2.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent);
511                 modalInstance.instance.open();
512
513                 new Promise((resolve) => {
514                     if (!this.ConnectionWizardServiceNg2.selectedMatch.capability.properties) {
515                         this.ComponentInstanceServiceNg2.getInstanceCapabilityProperties(scope.component, linkData.target, capability)
516                             .subscribe(() => {
517                                 resolve();
518                             }, (error) => {
519                             });
520                     } else {
521                         resolve();
522                     }
523                 }).then(() => {
524                     this.ModalServiceNg2.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent);
525                 })
526
527             }, (error) => {
528             });
529         };
530
531         scope.deleteRelation = (link: Cy.CollectionEdges) => {
532             scope.hideRelationMenu();
533
534             //if multiple edges selected, delete the VL itself so edges get deleted automatically
535             if (this._cy.$('edge:selected').length > 1) {
536                 this.NodesGraphUtils.deleteNode(this._cy, scope.component, this._cy.$('node:selected'));
537             } else {
538                 this.CompositionGraphLinkUtils.deleteLink(this._cy, scope.component, true, link);
539             }
540         };
541
542
543         document.onkeydown = (event) => {
544             let isModalExist = document.getElementsByTagName('body')[0].classList.contains('modal-open');
545
546             if (!scope.isViewOnly && !isModalExist) {
547
548                 switch (event.keyCode) {
549
550                     case 46: //delete
551                         scope.deleteSelectedElements();
552                         break;
553
554                     case 67: //ctrl+c : copy componentInstance
555                         if (event.ctrlKey && scope.component.isService()) {
556                             scope.copyComponentInstance();
557                         }
558                         break;
559                     case 86: // ctrl+v: paste componentInstance
560                         if (event.ctrlKey && scope.component.isService()) {
561                             let hidePasteObj = $(".w-canvas-content-paste");
562                             hidePasteObj.click();
563                         }
564                         break;
565
566                 }
567             }
568         };
569
570         scope.deleteSelectedElements = (): void => {
571             if (this._cy) {
572                 let nodesToDelete = this._cy.$('node:selected');
573                 let edgesSelected = this._cy.$('edge:selected');
574                 if (nodesToDelete.length + edgesSelected.length <= 0) {
575                     return;
576                 }
577                 let componentInstancetobeDele;
578                 let title: string = "Delete Confirmation";
579                 let message: string = "Are you sure you would like to delete selected elements?";
580                 if (nodesToDelete.size() == 1 && edgesSelected.size() == 0) {
581
582                     componentInstancetobeDele = nodesToDelete[0].data().componentInstance;
583                     let showName = nodesToDelete[0].data().componentInstance.name;
584                     message = "Are you sure you would like to delete" + " " + showName + "?";
585                 }
586
587                 let onOk = (): void => {
588                     if (nodesToDelete.size() == 1 && edgesSelected.size() == 0) {
589                         this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, componentInstancetobeDele);
590                     }
591                     else {
592                         this.NodesGraphUtils.batchDeleteNodes(this._cy, scope.component, nodesToDelete);
593                     }
594
595                 };
596
597                 this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
598             }
599
600         };
601
602         scope.copyComponentInstance = (): void => {
603             scope.origComponentId = scope.component.uniqueId;
604             if (scope.component.selectedInstance) {
605                 scope.origSelectedInstance = scope.component.selectedInstance;
606             }
607         };
608
609
610         scope.pasteComponentInstance = (event: IDragDropEvent): void => {
611             event.clientX = event.clientX ? event.clientX : this.cyBackgroundClickEvent ? this.cyBackgroundClickEvent.originalEvent.clientX : null;
612             event.clientY = event.clientY ? event.clientY : this.cyBackgroundClickEvent ? this.cyBackgroundClickEvent.originalEvent.clientY : null;
613
614             if (event.clientX == null || event.clientY == null) {
615                 return;
616             }
617             let offsetPosition = {
618                 x: event.clientX - GraphUIObjects.DIAGRAM_PALETTE_WIDTH_OFFSET,
619                 y: event.clientY - GraphUIObjects.DIAGRAM_HEADER_OFFSET
620             };
621
622             let mousePosition = this.commonGraphUtils.HTMLCoordsToCytoscapeCoords(this._cy.extent(), offsetPosition);
623             let newPositionX = mousePosition.x;
624             let newPositionY = mousePosition.y;
625             let origSelectedInstance = scope.origSelectedInstance;
626
627             let copyComponentInstance = `{               
628                 "posX":"${newPositionX}",
629                 "posY":"${newPositionY}",
630                 "name":"${origSelectedInstance.componentName}",
631                 "componentVersion":"${origSelectedInstance.componentVersion}",
632                 "originType":"${origSelectedInstance.originType}",
633                 "icon":"${origSelectedInstance.icon}",
634                 "componentUid":"${origSelectedInstance.componentUid}"
635             }`;
636
637             this.isComponentPasteValid(scope, this._cy, event, offsetPosition, origSelectedInstance);
638
639
640             let onSuccess = (response): void => {
641                 let success = (component: Component) => {
642                     scope.component.componentInstances = component.componentInstances;
643                     if (component.isService() && component.componentInstances.length > 0) {
644                         _.each(component.componentInstances, (instance) => {
645                             if (instance.uniqueId == response.componentInstance.uniqueId) {
646                                 let compositionGraphNode: CompositionCiNodeBase = this.NodesFactory.createNode(instance);
647                                 this.commonGraphUtils.addComponentInstanceNodeToGraph(this._cy, compositionGraphNode);
648                             }
649                         });
650                     }
651                     scope.isLoading = false;
652                 };
653                 scope.component.getComponent().then(success);
654             };
655             let onFailed = (error: any): void => {
656                 console.log(error);
657                 scope.isLoading = false;
658             };
659
660             if (scope.pasteValid) {
661                 scope.isLoading = true;
662                 scope.component.pasteMenuComponentInstance(origSelectedInstance.uniqueId, copyComponentInstance).then(onSuccess, onFailed);
663             }
664         };
665
666     }
667
668
669
670     private registerCytoscapeGraphEvents(scope: ICompositionGraphScope) {
671
672         this._cy.on('addedgemouseup', (event, data) => {
673             scope.relationMenuDirectiveObj = this.CompositionGraphLinkUtils.onLinkDrawn(this._cy, data.source, data.target);
674             if (scope.relationMenuDirectiveObj != null) {
675                 this.ConnectionWizardServiceNg2.setRelationMenuDirectiveObj(scope.relationMenuDirectiveObj);
676                 this.ConnectionWizardServiceNg2.currentComponent = scope.component;
677                 //TODO: init with the selected values
678                 this.ConnectionWizardServiceNg2.selectedMatch = null;
679
680                 let steps: Array<StepModel> = [];
681                 let fromNodeName: string = scope.relationMenuDirectiveObj.fromNode.componentInstance.name;
682                 let toNodeName: string = scope.relationMenuDirectiveObj.toNode.componentInstance.name;
683                 steps.push(new StepModel(fromNodeName, FromNodeStepComponent));
684                 steps.push(new StepModel(toNodeName, ToNodeStepComponent));
685                 steps.push(new StepModel('Properties', PropertiesStepComponent));
686                 let wizardTitle = 'Connect: ' + fromNodeName + ' to ' + toNodeName;
687                 let modalInstance = this.ModalServiceNg2.createMultiStepsWizard(wizardTitle, steps, scope.createLinkFromMenu, ConnectionWizardHeaderComponent);
688                 modalInstance.instance.open();
689             }
690         });
691         this._cy.on('tapstart', 'node', (event: Cy.EventObject) => {
692             scope.isOnDrag = true;
693             this._currentlyCLickedNodePosition = angular.copy(event.cyTarget[0].position()); //update node position on drag
694             if (event.cyTarget.data().isUcpe) {
695                 this._cy.nodes('.ucpe-cp').unlock();
696                 event.cyTarget.style('opacity', 0.5);
697             }
698         });
699
700         this._cy.on('drag', 'node', (event: Cy.EventObject) => {
701
702             if (event.cyTarget.data().isDraggable) {
703                 event.cyTarget.style({ 'overlay-opacity': 0.24 });
704                 if (this.GeneralGraphUtils.isValidDrop(this._cy, event.cyTarget)) {
705                     event.cyTarget.style({ 'overlay-color': GraphColors.NODE_BACKGROUND_COLOR });
706                 } else {
707                     event.cyTarget.style({ 'overlay-color': GraphColors.NODE_OVERLAPPING_BACKGROUND_COLOR });
708                 }
709             }
710
711             if (event.cyTarget.data().isUcpe) {
712                 let pos = event.cyTarget.position();
713
714                 this._cy.nodes('[?isInsideGroup]').positions((i, node) => {
715                     return {
716                         x: pos.x + node.data("ucpeOffset").x,
717                         y: pos.y + node.data("ucpeOffset").y
718                     }
719                 });
720             }
721         });
722
723         this._cy.on('handlemouseover', (event, payload) => {
724
725             if (payload.node.grabbed() || this._cy.scratch('_edge_editation_highlights') === true) { //no need to add opacity while we are dragging and hovering othe nodes- or if opacity was already calculated for these nodes
726                 return;
727             }
728
729             if (scope.zoneTagMode) {
730                 scope.zoneTagMode = scope.zones[scope.activeZoneInstance.type].getHoverTagModeId();
731                 return;
732             }
733
734             let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes());
735             let nodesLinks = this.GeneralGraphUtils.getAllCompositionCiLinks(this._cy);
736
737             let linkableNodes = this.commonGraphUtils.getLinkableNodes(this._cy, payload.node);
738             let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findMatchingNodes(payload.node.data().componentInstance, linkableNodes, nodesLinks);
739             this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
740             this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy, payload.node.data());
741
742             this._cy.scratch()._edge_editation_highlights = true;
743         });
744
745         this._cy.on('handlemouseout', () => {
746             if (scope.zoneTagMode) {
747                 scope.zoneTagMode = scope.zones[scope.activeZoneInstance.type].getTagModeId();
748                 return;
749             }
750             if (this._cy.scratch('_edge_editation_highlights') === true) {
751                 this._cy.removeScratch('_edge_editation_highlights');
752                 this._cy.emit('hidehandles');
753                 this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
754             }
755         });
756
757
758         this._cy.on('tapend', (event: Cy.EventObject) => {
759             scope.isOnDrag = false;
760             if (scope.zoneTagMode) {
761                 return;
762             }
763             if (event.cyTarget === this._cy) { //On Background clicked
764                 if (this._cy.$('node:selected').length === 0) { //if the background click but not dragged
765                     if (scope.activeZoneInstance) {
766                         scope.unsetActiveZoneInstance();
767                     }
768                     this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED);
769                 }
770                 this.cyBackgroundClickEvent = event;
771                 scope.hideRelationMenu();
772             }
773
774             else if (event.cyTarget.isEdge()) { //On Edge clicked
775                 this.CompositionGraphLinkUtils.handleLinkClick(this._cy, event);
776                 if (event.cyTarget.data().type === CompositionCiServicePathLink.LINK_TYPE) {
777                     return;
778                 }
779                 this.openModifyLinkMenu(scope, this.CompositionGraphLinkUtils.getModifyLinkMenu(event.cyTarget[0], event), 6000);
780             }
781
782             else { //On Node clicked
783
784                 this._cy.nodes(':grabbed').style({ 'overlay-opacity': 0 });
785
786                 let isUcpe: boolean = event.cyTarget.data().isUcpe;
787                 let newPosition = event.cyTarget[0].position();
788                 //node position changed (drop after drag event) - we need to update position
789                 if (this._currentlyCLickedNodePosition.x !== newPosition.x || this._currentlyCLickedNodePosition.y !== newPosition.y) {
790                     let nodesMoved: Cy.CollectionNodes = this._cy.$(':grabbed');
791                     if (isUcpe) {
792                         nodesMoved = nodesMoved.add(this._cy.nodes('[?isInsideGroup]:free')); //'child' nodes will not be recognized as "grabbed" elements within cytoscape. manually add them to collection of nodes moved.
793                     }
794                     this.NodesGraphUtils.onNodesPositionChanged(this._cy, scope.component, nodesMoved);
795                 } else {
796                     this.$log.debug('composition-graph::onNodeSelectedEvent:: fired');
797                     if (scope.activeZoneInstance) {
798                         scope.unsetActiveZoneInstance();
799                     }
800                     scope.$apply(() => {
801                         this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_NODE_SELECTED, event.cyTarget.data().componentInstance);
802                     });
803                 }
804
805                 if (isUcpe) {
806                     this._cy.nodes('.ucpe-cp').lock();
807                     event.cyTarget.style('opacity', 1);
808                 }
809
810             }
811         });
812
813         this._cy.on('boxselect', 'node', (event: Cy.EventObject) => {
814             scope.unsetActiveZoneInstance();
815             this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_NODE_SELECTED, event.cyTarget.data().componentInstance);
816         });
817
818         this._cy.on('canvasredraw', (event: Cy.EventObject) => {
819             if (scope.zoneTagMode) {
820                 this.compositionGraphZoneUtils.showZoneTagIndications(this._cy, scope.activeZoneInstance);
821             }
822         });
823
824         this._cy.on('handletagclick', (event: Cy.EventObject, eventData: any) => {
825             this.compositionGraphZoneUtils.handleTagClick(this._cy, scope.activeZoneInstance, eventData.nodeId);
826
827
828         });
829     }
830
831     private openModifyLinkMenu = (scope: ICompositionGraphScope, linkMenuObject: LinkMenu, timeOutInMilliseconds?: number) => {
832         scope.hideRelationMenu();
833         this.$timeout(() => {
834             scope.canvasMenuProps = {
835                 open: true,
836                 styleClass: 'w-sdc-canvas-menu-list',
837                 items: [],
838                 position: {
839                     x: `${linkMenuObject.position.x}px`,
840                     y: `${linkMenuObject.position.y}px`
841                 }
842             };
843
844             if (this._cy.$('edge:selected').length === 1) {
845                 scope.canvasMenuProps.items.push({
846                     contents: 'View',
847                     styleClass: 'w-sdc-canvas-menu-item-view',
848                     action: () => {
849                         scope.viewRelation(<Cy.CollectionEdges>linkMenuObject.link);
850                     }
851                 });
852             }
853             if (!scope.isViewOnly) {
854                 scope.canvasMenuProps.items.push({
855                     contents: 'Delete',
856                     styleClass: 'w-sdc-canvas-menu-item-delete',
857                     action: () => {
858                         scope.deleteRelation(<Cy.CollectionEdges>linkMenuObject.link);
859                     }
860                 });
861             }
862             scope.relationMenuTimeout = this.$timeout(() => {
863                 scope.hideRelationMenu();
864             }, timeOutInMilliseconds ? timeOutInMilliseconds : 6000);
865         });
866     };
867
868     private initGraphNodes(componentInstances: ComponentInstance[], isViewOnly: boolean) {
869
870
871         setTimeout(() => {
872             let handles = new CytoscapeEdgeEditation;
873             handles.init(this._cy);
874             if (!isViewOnly) { //Init nodes handle extension - enable dynamic links
875                 handles.initNodeEvents();
876                 handles.registerHandle(ComponentInstanceNodesStyle.getAddEdgeHandle());
877             }
878             handles.registerHandle(ComponentInstanceNodesStyle.getTagHandle());
879             handles.registerHandle(ComponentInstanceNodesStyle.getTaggedPolicyHandle());
880             handles.registerHandle(ComponentInstanceNodesStyle.getTaggedGroupHandle());
881         }, 0);
882
883
884         _.each(componentInstances, (instance) => {
885             let compositionGraphNode: CompositionCiNodeBase = this.NodesFactory.createNode(instance);
886             this.commonGraphUtils.addComponentInstanceNodeToGraph(this._cy, compositionGraphNode);
887         });
888     }
889
890     private initDropZone(scope: ICompositionGraphScope) {
891
892         if (scope.isViewOnly) {
893             return;
894         }
895         scope.dropCallback = (event: IDragDropEvent) => {
896             this.$log.debug(`composition-graph::dropCallback:: fired`);
897             this.CompositionGraphPaletteUtils.addNodeFromPalette(this._cy, event, scope.component);
898         };
899
900         scope.verifyDrop = (event: JQueryEventObject) => {
901
902             if (!this.dragElement || this.dragElement.hasClass('red')) {
903                 return false;
904             }
905             return true;
906         };
907
908         scope.beforeDropCallback = (event: IDragDropEvent): ng.IPromise<void> => {
909             let deferred: ng.IDeferred<void> = this.$q.defer<void>();
910             if (this.dragElement.hasClass('red')) {
911                 deferred.reject();
912             } else {
913                 deferred.resolve();
914             }
915
916             return deferred.promise;
917         }
918     }
919
920
921     private initZones = (scope: ICompositionGraphScope): void => {
922         scope.zones = this.compositionGraphZoneUtils.createCompositionZones();
923
924
925         scope.zoneMinimizeToggle = (zoneType: ZoneInstanceType): void => {
926             scope.zones[zoneType].minimized = !scope.zones[zoneType].minimized;
927         };
928
929         scope.zoneInstanceModeChanged = (newMode: ZoneInstanceMode, instance: ZoneInstance, zoneId: ZoneInstanceType): void => {
930             if (scope.zoneTagMode) { //we're in tag mode.
931                 if (instance == scope.activeZoneInstance && newMode == ZoneInstanceMode.NONE) { //we want to turn tag mode off.
932                     scope.zoneTagMode = null;
933                     scope.activeZoneInstance.mode = ZoneInstanceMode.SELECTED;
934                     this.compositionGraphZoneUtils.endCyTagMode(this._cy);
935                     this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_CANVAS_TAG_END, instance);
936
937                 }
938             } else {
939                 if (instance != scope.activeZoneInstance || (instance == scope.activeZoneInstance && newMode > ZoneInstanceMode.HOVER)) { //when active zone instance gets hover/none,dont actually change mode, just show/hide indications
940                     instance.mode = newMode;
941                 }
942
943                 if (newMode == ZoneInstanceMode.NONE) {
944                     this.compositionGraphZoneUtils.hideZoneTagIndications(this._cy);
945                     if (scope.zones[ZoneInstanceType.GROUP]) {
946                         this.compositionGraphZoneUtils.hideGroupZoneIndications(scope.zones[ZoneInstanceType.GROUP].instances);
947                     }
948                 }
949                 if (newMode >= ZoneInstanceMode.HOVER) {
950                     this.compositionGraphZoneUtils.showZoneTagIndications(this._cy, instance);
951                     if (instance.type == ZoneInstanceType.POLICY && scope.zones[ZoneInstanceType.GROUP]) {
952                         this.compositionGraphZoneUtils.showGroupZoneIndications(scope.zones[ZoneInstanceType.GROUP].instances, instance);
953                     }
954                 }
955                 if (newMode >= ZoneInstanceMode.SELECTED) {
956                     this._cy.$('node:selected').unselect();
957                     if (scope.activeZoneInstance && scope.activeZoneInstance != instance && newMode >= ZoneInstanceMode.SELECTED) {
958                         scope.activeZoneInstance.mode = ZoneInstanceMode.NONE;
959                     }
960                     scope.activeZoneInstance = instance;
961                     this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ZONE_INSTANCE_SELECTED, instance);
962                 }
963                 if (newMode == ZoneInstanceMode.TAG) {
964                     this.compositionGraphZoneUtils.startCyTagMode(this._cy);
965                     scope.zoneTagMode = scope.zones[zoneId].getTagModeId();
966                     this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_CANVAS_TAG_START, zoneId);
967                 }
968             }
969         };
970
971         scope.zoneInstanceTagged = (taggedInstance: ZoneInstance) => {
972             scope.activeZoneInstance.addOrRemoveAssignment(taggedInstance.instanceData.uniqueId, ZoneInstanceAssignmentType.GROUPS);
973             let newHandle: string = this.compositionGraphZoneUtils.getCorrectHandleForNode(taggedInstance.instanceData.uniqueId, scope.activeZoneInstance);
974             taggedInstance.showHandle(newHandle);
975         }
976
977         scope.zoneBackgroundClicked = (): void => {
978             if (!scope.zoneTagMode && scope.activeZoneInstance) {
979                 scope.unsetActiveZoneInstance();
980             }
981         };
982
983         scope.zoneAssignmentSaveStart = () => {
984             this.LoaderService.showLoader('composition-graph');
985         }
986
987         scope.zoneAssignmentSaveComplete = (success: boolean) => {
988             this.LoaderService.hideLoader('composition-graph');
989             if (!success) {
990                 this.GeneralGraphUtils.showUpdateFailure();
991             }
992         };
993
994         scope.unsetActiveZoneInstance = (): void => {
995             if (scope.activeZoneInstance) {
996                 scope.activeZoneInstance.mode = ZoneInstanceMode.NONE;
997                 scope.activeZoneInstance = null;
998                 scope.zoneTagMode = null;
999                 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED);
1000             }
1001         };
1002     };
1003     private isComponentPasteValid(scope: ICompositionGraphScope, cy: Cy.Instance, event: IDragDropEvent, offsetPosition: Cy.Position, origSelectedInstance: ComponentInstance) {
1004         let bbox = this._getNodeBBox(cy, event, origSelectedInstance, offsetPosition);
1005
1006         if (this.GeneralGraphUtils.isPaletteDropValid(cy, bbox, origSelectedInstance)) {
1007             scope.pasteValid = true;
1008         } else {
1009             scope.pasteValid = false;
1010         }
1011     }
1012
1013     private _getNodeBBox(cy: Cy.Instance, event: IDragDropEvent, origSelectedInstance: ComponentInstance, position?: Cy.Position) {
1014         let bbox = <Cy.BoundingBox>{};
1015         if (!position) {
1016             position = this.commonGraphUtils.getCytoscapeNodePosition(cy, event);
1017         }
1018         let cushionWidth: number = 40;
1019         let cushionHeight: number = 40;
1020
1021         bbox.x1 = position.x - cushionWidth / 2;
1022         bbox.y1 = position.y - cushionHeight / 2;
1023         bbox.x2 = position.x + cushionWidth / 2;
1024         bbox.y2 = position.y + cushionHeight / 2;
1025         return bbox;
1026     }
1027
1028     public static factory = ($q,
1029         $log,
1030         $timeout,
1031         NodesFactory,
1032         LinksGraphUtils,
1033         GeneralGraphUtils,
1034         ComponentInstanceFactory,
1035         NodesGraphUtils,
1036         EventListenerService,
1037         ComponentFactory,
1038         LoaderService,
1039         CommonGraphUtils,
1040         MatchCapabilitiesRequirementsUtils,
1041         CompositionGraphPaletteUtils,
1042         CompositionGraphZoneUtils,
1043         ComponentServiceNg2,
1044         ModalService,
1045         ConnectionWizardService,
1046         ComponentInstanceServiceNg2,
1047         ServicePathGraphUtils,
1048         ModalsHandler) => {
1049         return new CompositionGraph(
1050             $q,
1051             $log,
1052             $timeout,
1053             NodesFactory,
1054             LinksGraphUtils,
1055             GeneralGraphUtils,
1056             ComponentInstanceFactory,
1057             NodesGraphUtils,
1058             EventListenerService,
1059             ComponentFactory,
1060             LoaderService,
1061             CommonGraphUtils,
1062             MatchCapabilitiesRequirementsUtils,
1063             CompositionGraphPaletteUtils,
1064             CompositionGraphZoneUtils,
1065             ComponentServiceNg2,
1066             ModalService,
1067             ConnectionWizardService,
1068             ComponentInstanceServiceNg2,
1069             ServicePathGraphUtils,
1070             ModalsHandler);
1071     }
1072 }
1073
1074 CompositionGraph.factory.$inject = [
1075     '$q',
1076     '$log',
1077     '$timeout',
1078     'NodesFactory',
1079     'CompositionGraphLinkUtils',
1080     'CompositionGraphGeneralUtils',
1081     'ComponentInstanceFactory',
1082     'CompositionGraphNodesUtils',
1083     'EventListenerService',
1084     'ComponentFactory',
1085     'LoaderService',
1086     'CommonGraphUtils',
1087     'MatchCapabilitiesRequirementsUtils',
1088     'CompositionGraphPaletteUtils',
1089     'CompositionGraphZoneUtils',
1090     'ComponentServiceNg2',
1091     'ModalServiceNg2',
1092     'ConnectionWizardServiceNg2',
1093     'ComponentInstanceServiceNg2',
1094     'ServicePathGraphUtils',
1095     'ModalsHandler'
1096 ];