2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 import * as _ from "lodash";
31 CompositionCiNodeBase,
38 import {ComponentInstanceFactory, ComponentFactory, GRAPH_EVENTS, GraphColors} 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";
65 ZoneInstance, ZoneInstanceMode, ZoneInstanceType,
66 ZoneInstanceAssignmentType
67 } from "app/models/graph/zones/zone-instance";
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";
76 export interface ICompositionGraphScope extends ng.IScope {
86 activeZoneInstance:ZoneInstance;
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;
96 // Link menu - create link menu
97 relationMenuDirectiveObj:ConnectRelationModel;
98 isLinkMenuOpen:boolean;
99 createLinkFromMenu:(chosenMatch:Match, vl:Component)=>void;
100 saveChangedCapabilityProperties:()=>Promise<PropertyBEModel[]>;
102 //modify link menu - for now only delete menu
103 relationMenuTimeout:ng.IPromise<any>;
104 linkMenuObject:LinkMenu;
107 //left palette functions callbacks
108 dropCallback(event:JQueryEventObject, ui:any):void;
109 beforeDropCallback(event:IDragDropEvent):void;
110 verifyDrop(event:JQueryEventObject, ui:any):void;
113 viewRelation(link:Cy.CollectionEdges):void;
114 deleteRelation(link:Cy.CollectionEdges):void;
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;
126 createOrUpdateServicePath(data:any):void;
127 deletePathsOnCy():void;
128 drawPathOnCy(data:ForwardingPath):void;
129 selectedPathId:string;
131 /*//asset popover menu
132 assetPopoverObj:AssetPopoverObj;
133 assetPopoverOpen:boolean;
134 hideAssetPopover():void;
135 deleteNode(nodeId:string):void;*/
138 export class CompositionGraph implements ng.IDirective {
139 private _cy:Cy.Instance;
140 private _currentlyCLickedNodePosition:Cy.Position;
141 // private $document:JQuery = $(document);
142 private dragElement:JQuery;
143 private dragComponent:ComponentInstance;
145 constructor(private $q:ng.IQService,
146 private $log:ng.ILogService,
147 private $timeout:ng.ITimeoutService,
148 private NodesFactory:NodesFactory,
149 private CompositionGraphLinkUtils:CompositionGraphLinkUtils,
150 private GeneralGraphUtils:CompositionGraphGeneralUtils,
151 private ComponentInstanceFactory:ComponentInstanceFactory,
152 private NodesGraphUtils:CompositionGraphNodesUtils,
153 private eventListenerService:EventListenerService,
154 private ComponentFactory:ComponentFactory,
155 private LoaderService:LoaderService,
156 private commonGraphUtils:CommonGraphUtils,
157 private matchCapabilitiesRequirementsUtils:MatchCapabilitiesRequirementsUtils,
158 private CompositionGraphPaletteUtils:CompositionGraphPaletteUtils,
159 private compositionGraphZoneUtils:CompositionGraphZoneUtils,
160 private ComponentServiceNg2:ComponentServiceNg2,
161 private ModalServiceNg2:ModalService,
162 private ConnectionWizardServiceNg2:ConnectionWizardService,
163 private ComponentInstanceServiceNg2:ComponentInstanceServiceNg2,
164 private servicePathGraphUtils:ServicePathGraphUtils) {
169 template = require('./composition-graph.html');
176 link = (scope:ICompositionGraphScope, el:JQuery) => {
177 this.loadGraph(scope, el);
179 if (!scope.component.groupInstances || !scope.component.policies) {
180 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED, () => {
181 this.loadGraphData(scope);
184 this.loadGraphData(scope);
188 scope.$on('$destroy', () => {
190 _.forEach(GRAPH_EVENTS, (event) => {
191 this.eventListenerService.unRegisterObserver(event);
193 this.eventListenerService.unRegisterObserver(EVENTS.SHOW_LOADER_EVENT + 'composition-graph');
194 this.eventListenerService.unRegisterObserver(EVENTS.HIDE_LOADER_EVENT + 'composition-graph');
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);
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);
219 private initGraph(graphEl:JQuery, isViewOnly:boolean) {
221 this._cy = cytoscape({
223 style: ComponentInstanceNodesStyle.getCompositionGraphStyle(),
224 zoomingEnabled: true,
227 userZoomingEnabled: false,
228 userPanningEnabled: true,
229 selectionType: 'single',
230 boxSelectionEnabled: true,
231 autolock: isViewOnly,
232 autoungrabify: isViewOnly
236 private initViewMode(isViewOnly:boolean) {
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();
249 private registerCustomEvents(scope:ICompositionGraphScope, el:JQuery) {
251 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_GROUP_INSTANCE_UPDATE, (groupInstance:GroupInstance) => {
252 this.compositionGraphZoneUtils.findAndUpdateZoneInstanceData(scope.zones, groupInstance);
253 this.GeneralGraphUtils.showGroupUpdateSuccess();
256 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_POLICY_INSTANCE_UPDATE, (policyInstance: PolicyInstance) => {
257 this.compositionGraphZoneUtils.findAndUpdateZoneInstanceData(scope.zones, policyInstance);
258 this.GeneralGraphUtils.showPolicyUpdateSuccess();
261 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_IN, (leftPaletteComponent:LeftPaletteComponent) => {
262 if (scope.isOnDrag) {
266 this.$log.info(`composition-graph::registerEventServiceEvents:: palette hover on component: ${leftPaletteComponent.uniqueId}`);
268 let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes());
269 let nodesLinks = this.GeneralGraphUtils.getAllCompositionCiLinks(this._cy);
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);
275 this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
276 this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy);
281 //----------------------- ORIT TO FIX------------------------//
283 this.ComponentServiceNg2.getCapabilitiesAndRequirements(leftPaletteComponent.componentType, leftPaletteComponent.uniqueId).subscribe((response:ComponentGenericResponse) => {
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);
293 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ADD_ZONE_INSTANCE_FROM_PALETTE, (component:Component, paletteComponent:LeftPaletteComponent, startPosition:Point) => {
295 let zoneType:ZoneInstanceType = this.compositionGraphZoneUtils.getZoneTypeForPaletteComponent(paletteComponent.categoryType);
296 this.compositionGraphZoneUtils.showZone(scope.zones[zoneType]);
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);
304 this.LoaderService.hideLoader('composition-graph');
308 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_OUT, () => {
310 this._cy.emit('hidehandles');
311 this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
314 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_START, (dragElement, dragComponent) => {
316 this.dragElement = dragElement;
317 this.dragComponent = this.ComponentInstanceFactory.createComponentInstanceFromComponent(dragComponent);
320 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_ACTION, (event:IDragDropEvent) => {
321 this.CompositionGraphPaletteUtils.onComponentDrag(this._cy, event, this.dragElement, this.dragComponent);
325 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, (component:ComponentInstance) => {
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
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);
339 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_ZONE_INSTANCE, (deletedInstance:UIZoneInstanceObject) => {
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);
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);
351 this.eventListenerService.notifyObservers(EVENTS.UPDATE_PANEL);
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);
359 // not in use; commenting out
360 // this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_MULTIPLE_COMPONENTS, () => {
362 // this._cy.$('node:selected').each((i:number, node:Cy.CollectionNodes) => {
363 // this.NodesGraphUtils.deleteNode(this._cy, scope.component, node);
368 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_EDGE, (releaseLoading:boolean, linksToDelete:Cy.CollectionEdges) => {
369 this.CompositionGraphLinkUtils.deleteLink(this._cy, scope.component, releaseLoading, linksToDelete);
372 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_INSERT_NODE_TO_UCPE, (node:Cy.CollectionNodes, ucpe:Cy.CollectionNodes, updateExistingNode:boolean) => {
374 this.commonGraphUtils.initUcpeChildData(node, ucpe);
375 //check if item is a VL, and if so, skip adding the binding to ucpe
376 if (!(node.data() instanceof CompositionCiNodeVl)) {
377 this.CompositionGraphLinkUtils.createVfToUcpeLink(scope.component, this._cy, ucpe.data(), node.data()); //create link from the node to the ucpe
380 if (updateExistingNode) {
381 let vlsPendingDeletion:Cy.CollectionNodes = this.NodesGraphUtils.deleteNodeVLsUponMoveToOrFromUCPE(scope.component, node.cy(), node); //delete connected VLs that no longer have 2 links
382 this.CompositionGraphLinkUtils.deleteLinksWhenNodeMovedFromOrToUCPE(scope.component, node.cy(), node, vlsPendingDeletion); //delete all connected links if needed
383 this.GeneralGraphUtils.pushUpdateComponentInstanceActionToQueue(scope.component, true, node.data().componentInstance); //update componentInstance position
388 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_REMOVE_NODE_FROM_UCPE, (node:Cy.CollectionNodes, ucpe:Cy.CollectionNodes) => {
389 this.commonGraphUtils.removeUcpeChildData(node);
390 let vlsPendingDeletion:Cy.CollectionNodes = this.NodesGraphUtils.deleteNodeVLsUponMoveToOrFromUCPE(scope.component, node.cy(), node);
391 this.CompositionGraphLinkUtils.deleteLinksWhenNodeMovedFromOrToUCPE(scope.component, node.cy(), node, vlsPendingDeletion); //delete all connected links if needed
392 this.GeneralGraphUtils.pushUpdateComponentInstanceActionToQueue(scope.component, true, node.data().componentInstance); //update componentInstance position
395 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_VERSION_CHANGED, (component:Component) => {
396 scope.component = component;
397 this._cy.elements().remove();
398 this.loadGraphData(scope);
402 scope.zoom = (zoomIn:boolean):void => {
403 let currentZoom:number = this._cy.zoom();
405 this.GeneralGraphUtils.zoomGraphTo(this._cy, currentZoom + .1);
407 this.GeneralGraphUtils.zoomGraphTo(this._cy, currentZoom - .1);
412 scope.zoomAllWithoutSidebar = () => {
413 scope.withSidebar = false;
414 setTimeout(() => { //wait for sidebar changes to take effect before zooming
415 this.GeneralGraphUtils.zoomAll(this._cy);
419 scope.getAutoCompleteValues = (searchTerm:string) => {
420 if (searchTerm.length > 1) { //US requirement: only display search results after 2nd letter typed.
421 let nodes:Cy.CollectionNodes = this.NodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
422 scope.componentInstanceNames = _.map(nodes, node => node.data('name'));
424 scope.componentInstanceNames = [];
428 scope.highlightSearchMatches = (searchTerm:string) => {
429 this.NodesGraphUtils.highlightMatchingNodesByName(this._cy, searchTerm);
430 let matchingNodes:Cy.CollectionNodes = this.NodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
431 this.GeneralGraphUtils.zoomAll(this._cy, matchingNodes);
434 scope.saveChangedCapabilityProperties = ():Promise<PropertyBEModel[]> => {
435 return new Promise<PropertyBEModel[]>((resolve) => {
436 const capabilityPropertiesBE:PropertyBEModel[] = this.ConnectionWizardServiceNg2.changedCapabilityProperties.map((prop) => {
437 prop.value = prop.getJSONValue();
438 const propBE = new PropertyBEModel(prop);
439 propBE.parentUniqueId = this.ConnectionWizardServiceNg2.selectedMatch.relationship.relation.capabilityOwnerId;
442 if (capabilityPropertiesBE.length > 0) {
443 // if there are capability properties to update, then first update capability properties and then resolve promise
444 this.ComponentInstanceServiceNg2
445 .updateInstanceCapabilityProperties(
447 this.ConnectionWizardServiceNg2.selectedMatch.toNode,
448 this.ConnectionWizardServiceNg2.selectedMatch.capability,
449 capabilityPropertiesBE
451 .subscribe((response) => {
452 console.log("Update resource instance capability properties response: ", response);
453 this.ConnectionWizardServiceNg2.changedCapabilityProperties = [];
454 resolve(capabilityPropertiesBE);
457 // no capability properties to update, immediately resolve promise
458 resolve(capabilityPropertiesBE);
463 scope.createLinkFromMenu = ():void => {
464 scope.isLinkMenuOpen = false;
466 scope.saveChangedCapabilityProperties().then(() => {
468 this.CompositionGraphLinkUtils
469 .createLinkFromMenu(this._cy, this.ConnectionWizardServiceNg2.selectedMatch, scope.component);
473 scope.hideRelationMenu = () => {
474 this.commonGraphUtils.safeApply(scope, () => {
475 delete scope.canvasMenuProps;
476 this.$timeout.cancel(scope.relationMenuTimeout);
480 scope.createOrUpdateServicePath = (data:any) => {
481 this.servicePathGraphUtils.createOrUpdateServicePath(scope, data);
483 scope.deletePathsOnCy = () => {
484 this.servicePathGraphUtils.deletePathsFromGraph(this._cy, <Service> scope.component);
486 scope.drawPathOnCy = (data:ForwardingPath) => {
487 this.servicePathGraphUtils.drawPath(this._cy, data, <Service> scope.component);
490 scope.viewRelation = (link:Cy.CollectionEdges) => {
491 scope.hideRelationMenu();
493 const linkData = link.data();
494 const sourceNode:CompositionCiNodeBase = link.source().data();
495 const targetNode:CompositionCiNodeBase = link.target().data();
496 const relationship:Relationship = linkData.relation.relationships[0];
498 scope.component.getRelationRequirementCapability(relationship, sourceNode.componentInstance, targetNode.componentInstance).then((objReqCap) => {
499 const capability = objReqCap.capability;
500 const requirement = objReqCap.requirement;
502 this.ConnectionWizardServiceNg2.currentComponent = scope.component;
503 this.ConnectionWizardServiceNg2.connectRelationModel = new ConnectRelationModel(sourceNode, targetNode, []);
504 this.ConnectionWizardServiceNg2.selectedMatch = new Match(requirement, capability, true, linkData.source, linkData.target);
505 this.ConnectionWizardServiceNg2.selectedMatch.relationship = relationship;
507 const title = `Connection Properties`;
508 const saveButton:ButtonModel = new ButtonModel('Save', 'blue', () => {
509 scope.saveChangedCapabilityProperties().then(() => {
510 this.ModalServiceNg2.closeCurrentModal();
513 const cancelButton:ButtonModel = new ButtonModel('Cancel', 'white', () => {
514 this.ModalServiceNg2.closeCurrentModal();
516 const modal = new ModalModel('xl', title, '', [saveButton, cancelButton]);
517 const modalInstance = this.ModalServiceNg2.createCustomModal(modal);
518 this.ModalServiceNg2.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent);
519 modalInstance.instance.open();
521 new Promise((resolve) => {
522 if (!this.ConnectionWizardServiceNg2.selectedMatch.capability.properties) {
523 this.ComponentInstanceServiceNg2.getInstanceCapabilityProperties(scope.component, linkData.target, capability)
532 this.ModalServiceNg2.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent);
539 scope.deleteRelation = (link:Cy.CollectionEdges) => {
540 scope.hideRelationMenu();
542 //if multiple edges selected, delete the VL itself so edges get deleted automatically
543 if (this._cy.$('edge:selected').length > 1) {
544 this.NodesGraphUtils.deleteNode(this._cy, scope.component, this._cy.$('node:selected'));
546 this.CompositionGraphLinkUtils.deleteLink(this._cy, scope.component, true, link);
551 scope.hideAssetPopover = ():void => {
553 this.commonGraphUtils.safeApply(scope, () => {
554 scope.assetPopoverOpen = false;
555 scope.assetPopoverObj = null;
559 scope.deleteNode = (nodeId:string):void => {
560 if (!scope.isViewOnly) {
561 this.NodesGraphUtils.confirmDeleteNode(nodeId, this._cy, scope.component);
562 //scope.hideAssetPopover();
567 private registerCytoscapeGraphEvents(scope:ICompositionGraphScope) {
569 this._cy.on('addedgemouseup', (event, data) => {
570 scope.relationMenuDirectiveObj = this.CompositionGraphLinkUtils.onLinkDrawn(this._cy, data.source, data.target);
571 if (scope.relationMenuDirectiveObj != null) {
572 this.ConnectionWizardServiceNg2.setRelationMenuDirectiveObj(scope.relationMenuDirectiveObj);
573 this.ConnectionWizardServiceNg2.currentComponent = scope.component;
574 //TODO: init with the selected values
575 this.ConnectionWizardServiceNg2.selectedMatch = null;
577 let steps:Array<StepModel> = [];
578 let fromNodeName:string = scope.relationMenuDirectiveObj.fromNode.componentInstance.name;
579 let toNodeName:string = scope.relationMenuDirectiveObj.toNode.componentInstance.name;
580 steps.push(new StepModel(fromNodeName, FromNodeStepComponent));
581 steps.push(new StepModel(toNodeName, ToNodeStepComponent));
582 steps.push(new StepModel('Properties', PropertiesStepComponent));
583 let wizardTitle = 'Connect: ' + fromNodeName + ' to ' + toNodeName;
584 let modalInstance = this.ModalServiceNg2.createMultiStepsWizard(wizardTitle, steps, scope.createLinkFromMenu, ConnectionWizardHeaderComponent);
585 modalInstance.instance.open();
588 // this.ModalServiceNg2.createMultiStepsWizard('Connect', )Connect
589 // scope.$apply(() => {
590 // scope.isLinkMenuOpen = true;
594 this._cy.on('tapstart', 'node', (event:Cy.EventObject) => {
595 scope.isOnDrag = true;
596 this._currentlyCLickedNodePosition = angular.copy(event.cyTarget[0].position()); //update node position on drag
597 if (event.cyTarget.data().isUcpe) {
598 this._cy.nodes('.ucpe-cp').unlock();
599 event.cyTarget.style('opacity', 0.5);
601 //scope.hideAssetPopover();
604 this._cy.on('drag', 'node', (event:Cy.EventObject) => {
606 if (event.cyTarget.data().isDraggable) {
607 event.cyTarget.style({'overlay-opacity': 0.24});
608 if (this.GeneralGraphUtils.isValidDrop(this._cy, event.cyTarget)) {
609 event.cyTarget.style({'overlay-color': GraphColors.NODE_BACKGROUND_COLOR});
611 event.cyTarget.style({'overlay-color': GraphColors.NODE_OVERLAPPING_BACKGROUND_COLOR});
615 if (event.cyTarget.data().isUcpe) {
616 let pos = event.cyTarget.position();
618 this._cy.nodes('[?isInsideGroup]').positions((i, node)=> {
620 x: pos.x + node.data("ucpeOffset").x,
621 y: pos.y + node.data("ucpeOffset").y
627 /* this._cy.on('mouseover', 'node', (event:Cy.EventObject) => {
628 if (!this._cy.scratch('_edge_editation_highlights')) {
629 this.commonGraphUtils.safeApply(scope, () => {
630 this.showNodePopoverMenu(scope, event.cyTarget[0]);
635 this._cy.on('mouseout', 'node', (event:Cy.EventObject) => {
636 scope.hideAssetPopover();
640 this._cy.on('handlemouseover', (event, payload) => {
642 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
646 if (scope.zoneTagMode) {
647 scope.zoneTagMode = scope.zones[scope.activeZoneInstance.type].getHoverTagModeId();
651 let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes());
652 let nodesLinks = this.GeneralGraphUtils.getAllCompositionCiLinks(this._cy);
654 let linkableNodes = this.commonGraphUtils.getLinkableNodes(this._cy, payload.node);
655 let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findMatchingNodes(payload.node.data().componentInstance, linkableNodes, nodesLinks);
656 this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
657 this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy, payload.node.data());
659 this._cy.scratch()._edge_editation_highlights = true;
660 /*scope.hideAssetPopover();*/
663 this._cy.on('handlemouseout', () => {
664 if (scope.zoneTagMode) {
665 scope.zoneTagMode = scope.zones[scope.activeZoneInstance.type].getTagModeId();
668 if (this._cy.scratch('_edge_editation_highlights') === true) {
669 this._cy.removeScratch('_edge_editation_highlights');
670 this._cy.emit('hidehandles');
671 this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
676 this._cy.on('tapend', (event:Cy.EventObject) => {
677 scope.isOnDrag = false;
678 if (scope.zoneTagMode) {
681 if (event.cyTarget === this._cy) { //On Background clicked
682 if (this._cy.$('node:selected').length === 0) { //if the background click but not dragged
683 if (scope.activeZoneInstance) {
684 scope.unsetActiveZoneInstance();
686 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED);
688 scope.hideRelationMenu();
691 else if (event.cyTarget.isEdge()) { //On Edge clicked
692 this.CompositionGraphLinkUtils.handleLinkClick(this._cy, event);
693 if (event.cyTarget.data().type === CompositionCiServicePathLink.LINK_TYPE) {
696 this.openModifyLinkMenu(scope, this.CompositionGraphLinkUtils.getModifyLinkMenu(event.cyTarget[0], event), 6000);
699 else { //On Node clicked
701 this._cy.nodes(':grabbed').style({'overlay-opacity': 0});
703 let isUcpe:boolean = event.cyTarget.data().isUcpe;
704 let newPosition = event.cyTarget[0].position();
705 //node position changed (drop after drag event) - we need to update position
706 if (this._currentlyCLickedNodePosition.x !== newPosition.x || this._currentlyCLickedNodePosition.y !== newPosition.y) {
707 let nodesMoved:Cy.CollectionNodes = this._cy.$(':grabbed');
709 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.
711 this.NodesGraphUtils.onNodesPositionChanged(this._cy, scope.component, nodesMoved);
713 this.$log.debug('composition-graph::onNodeSelectedEvent:: fired');
714 if (scope.activeZoneInstance) {
715 scope.unsetActiveZoneInstance();
718 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_NODE_SELECTED, event.cyTarget.data().componentInstance);
719 //open node popover menu
720 //this.showNodePopoverMenu(scope, event.cyTarget[0]);
725 this._cy.nodes('.ucpe-cp').lock();
726 event.cyTarget.style('opacity', 1);
732 this._cy.on('boxselect', 'node', (event:Cy.EventObject) => {
733 scope.unsetActiveZoneInstance();
734 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_NODE_SELECTED, event.cyTarget.data().componentInstance);
737 this._cy.on('canvasredraw', (event:Cy.EventObject) => {
738 if (scope.zoneTagMode) {
739 this.compositionGraphZoneUtils.showZoneTagIndications(this._cy, scope.activeZoneInstance);
743 this._cy.on('handletagclick', (event:Cy.EventObject, eventData:any) => {
744 this.compositionGraphZoneUtils.handleTagClick(this._cy, scope.activeZoneInstance, eventData.nodeId);
750 private openModifyLinkMenu = (scope:ICompositionGraphScope, linkMenuObject:LinkMenu, timeOutInMilliseconds?:number) => {
751 scope.hideRelationMenu();
752 this.$timeout(() => {
753 scope.canvasMenuProps = {
755 styleClass: 'w-sdc-canvas-menu-list',
758 x: `${linkMenuObject.position.x}px`,
759 y: `${linkMenuObject.position.y}px`
763 if (this._cy.$('edge:selected').length === 1) {
764 scope.canvasMenuProps.items.push({
766 styleClass: 'w-sdc-canvas-menu-item-view',
768 scope.viewRelation(<Cy.CollectionEdges>linkMenuObject.link);
772 if(!scope.isViewOnly){
773 scope.canvasMenuProps.items.push({
775 styleClass: 'w-sdc-canvas-menu-item-delete',
777 scope.deleteRelation(<Cy.CollectionEdges>linkMenuObject.link);
781 scope.relationMenuTimeout = this.$timeout(() => {
782 scope.hideRelationMenu();
783 }, timeOutInMilliseconds ? timeOutInMilliseconds : 6000);
787 private initGraphNodes(componentInstances:ComponentInstance[], isViewOnly:boolean) {
791 let handles = new CytoscapeEdgeEditation;
792 handles.init(this._cy);
793 if (!isViewOnly) { //Init nodes handle extension - enable dynamic links
794 handles.initNodeEvents();
795 handles.registerHandle(ComponentInstanceNodesStyle.getAddEdgeHandle());
797 handles.registerHandle(ComponentInstanceNodesStyle.getTagHandle());
798 handles.registerHandle(ComponentInstanceNodesStyle.getTaggedPolicyHandle());
799 handles.registerHandle(ComponentInstanceNodesStyle.getTaggedGroupHandle());
803 _.each(componentInstances, (instance) => {
804 let compositionGraphNode:CompositionCiNodeBase = this.NodesFactory.createNode(instance);
805 this.commonGraphUtils.addComponentInstanceNodeToGraph(this._cy, compositionGraphNode);
810 private initDropZone(scope:ICompositionGraphScope) {
812 if (scope.isViewOnly) {
815 scope.dropCallback = (event:IDragDropEvent) => {
816 this.$log.debug(`composition-graph::dropCallback:: fired`);
817 this.CompositionGraphPaletteUtils.addNodeFromPalette(this._cy, event, scope.component);
820 scope.verifyDrop = (event:JQueryEventObject) => {
822 if (!this.dragElement || this.dragElement.hasClass('red')) {
828 scope.beforeDropCallback = (event:IDragDropEvent):ng.IPromise<void> => {
829 let deferred:ng.IDeferred<void> = this.$q.defer<void>();
830 if (this.dragElement.hasClass('red')) {
836 return deferred.promise;
841 private initZones = (scope:ICompositionGraphScope):void => {
842 scope.zones = this.compositionGraphZoneUtils.createCompositionZones();
845 scope.zoneMinimizeToggle = (zoneType:ZoneInstanceType):void => {
846 scope.zones[zoneType].minimized = !scope.zones[zoneType].minimized;
849 scope.zoneInstanceModeChanged = (newMode:ZoneInstanceMode, instance:ZoneInstance, zoneId:ZoneInstanceType):void => {
850 if (scope.zoneTagMode) { //we're in tag mode.
851 if (instance == scope.activeZoneInstance && newMode == ZoneInstanceMode.NONE) { //we want to turn tag mode off.
852 scope.zoneTagMode = null;
853 scope.activeZoneInstance.mode = ZoneInstanceMode.SELECTED;
854 this.compositionGraphZoneUtils.endCyTagMode(this._cy);
855 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_CANVAS_TAG_END, instance);
859 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
860 instance.mode = newMode;
863 if (newMode == ZoneInstanceMode.NONE) {
864 this.compositionGraphZoneUtils.hideZoneTagIndications(this._cy);
865 if (scope.zones[ZoneInstanceType.GROUP]) {
866 this.compositionGraphZoneUtils.hideGroupZoneIndications(scope.zones[ZoneInstanceType.GROUP].instances);
869 if (newMode >= ZoneInstanceMode.HOVER) {
870 this.compositionGraphZoneUtils.showZoneTagIndications(this._cy, instance);
871 if (instance.type == ZoneInstanceType.POLICY && scope.zones[ZoneInstanceType.GROUP]) {
872 this.compositionGraphZoneUtils.showGroupZoneIndications(scope.zones[ZoneInstanceType.GROUP].instances, instance);
875 if (newMode >= ZoneInstanceMode.SELECTED) {
876 this._cy.$('node:selected').unselect();
877 if (scope.activeZoneInstance && scope.activeZoneInstance != instance && newMode >= ZoneInstanceMode.SELECTED) {
878 scope.activeZoneInstance.mode = ZoneInstanceMode.NONE;
880 scope.activeZoneInstance = instance;
881 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ZONE_INSTANCE_SELECTED, instance);
883 if (newMode == ZoneInstanceMode.TAG) {
884 this.compositionGraphZoneUtils.startCyTagMode(this._cy);
885 scope.zoneTagMode = scope.zones[zoneId].getTagModeId();
886 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_CANVAS_TAG_START, zoneId);
891 scope.zoneInstanceTagged = (taggedInstance:ZoneInstance) => {
892 scope.activeZoneInstance.addOrRemoveAssignment(taggedInstance.instanceData.uniqueId, ZoneInstanceAssignmentType.GROUPS);
893 let newHandle:string = this.compositionGraphZoneUtils.getCorrectHandleForNode(taggedInstance.instanceData.uniqueId, scope.activeZoneInstance);
894 taggedInstance.showHandle(newHandle);
897 scope.zoneBackgroundClicked = ():void => {
898 if (!scope.zoneTagMode && scope.activeZoneInstance) {
899 scope.unsetActiveZoneInstance();
903 scope.zoneAssignmentSaveStart = () => {
904 this.LoaderService.showLoader('composition-graph');
907 scope.zoneAssignmentSaveComplete = (success:boolean) => {
908 this.LoaderService.hideLoader('composition-graph');
910 this.GeneralGraphUtils.showUpdateFailure();
914 scope.unsetActiveZoneInstance = ():void => {
915 if(scope.activeZoneInstance){
916 scope.activeZoneInstance.mode = ZoneInstanceMode.NONE;
917 scope.activeZoneInstance = null;
918 scope.zoneTagMode = null;
919 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED);
925 public static factory = ($q,
931 ComponentInstanceFactory,
933 EventListenerService,
937 MatchCapabilitiesRequirementsUtils,
938 CompositionGraphPaletteUtils,
939 CompositionGraphZoneUtils,
942 ConnectionWizardService,
943 ComponentInstanceServiceNg2,
944 ServicePathGraphUtils) => {
945 return new CompositionGraph(
952 ComponentInstanceFactory,
954 EventListenerService,
958 MatchCapabilitiesRequirementsUtils,
959 CompositionGraphPaletteUtils,
960 CompositionGraphZoneUtils,
963 ConnectionWizardService,
964 ComponentInstanceServiceNg2,
965 ServicePathGraphUtils);
969 CompositionGraph.factory.$inject = [
974 'CompositionGraphLinkUtils',
975 'CompositionGraphGeneralUtils',
976 'ComponentInstanceFactory',
977 'CompositionGraphNodesUtils',
978 'EventListenerService',
982 'MatchCapabilitiesRequirementsUtils',
983 'CompositionGraphPaletteUtils',
984 'CompositionGraphZoneUtils',
985 'ComponentServiceNg2',
987 'ConnectionWizardServiceNg2',
988 'ComponentInstanceServiceNg2',
989 'ServicePathGraphUtils'