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";
34 CompositionCiNodeBase,
42 import {ComponentInstanceFactory, ComponentFactory, GRAPH_EVENTS, GraphColors} from "app/utils";
43 import {EventListenerService, LoaderService} from "app/services";
44 import {CompositionGraphLinkUtils} from "./utils/composition-graph-links-utils";
45 import {CompositionGraphGeneralUtils} from "./utils/composition-graph-general-utils";
46 import {CompositionGraphNodesUtils} from "./utils/composition-graph-nodes-utils";
47 import {CommonGraphUtils} from "../common/common-graph-utils";
48 import {MatchCapabilitiesRequirementsUtils} from "./utils/match-capability-requierment-utils";
49 import {CompositionGraphPaletteUtils} from "./utils/composition-graph-palette-utils";
50 import {ComponentInstanceNodesStyle} from "../common/style/component-instances-nodes-style";
51 import {CytoscapeEdgeEditation} from 'third-party/cytoscape.js-edge-editation/CytoscapeEdgeEditation.js';
52 import {ComponentServiceNg2} from "../../../ng2/services/component-services/component.service";
53 import {ComponentGenericResponse} from "../../../ng2/services/responses/component-generic-response";
54 import {ModalService} from "../../../ng2/services/modal.service";
56 import {ConnectionWizardService} from "../../../ng2/pages/connection-wizard/connection-wizard.service";
57 import {StepModel} from "../../../models/wizard-step";
58 import {FromNodeStepComponent} from "app/ng2/pages/connection-wizard/from-node-step/from-node-step.component";
59 import {PropertiesStepComponent} from "app/ng2/pages/connection-wizard/properties-step/properties-step.component";
60 import {ToNodeStepComponent} from "app/ng2/pages/connection-wizard/to-node-step/to-node-step.component";
61 import {ConnectionWizardHeaderComponent} from "app/ng2/pages/connection-wizard/connection-wizard-header/connection-wizard-header.component";
62 import {ConnectionPropertiesViewComponent} from "../../../ng2/pages/connection-wizard/connection-properties-view/connection-properties-view.component";
63 import {ComponentInstanceServiceNg2} from "../../../ng2/services/component-instance-services/component-instance.service";
64 import {EVENTS} from "../../../utils/constants";
65 import {PropertyBEModel} from "../../../models/properties-inputs/property-be-model";
66 import {ComponentType} from "app/utils";
67 import {ForwardingPath} from "app/models/forwarding-path";
68 import {ServicePathGraphUtils} from "./utils/composition-graph-service-path-utils";
69 import {CompositionCiServicePathLink} from "app/models/graph/graph-links/composition-graph-links/composition-ci-service-path-link";
70 import { ZoneConfig, ZoneInstanceConfig, ZoneInstanceMode } from "app/models/graph/zones/zone-child";
71 import { PoliciesService } from "app/ng2/services/policies.service";
72 import { PaletteAnimationComponent } from "app/ng2/components/ui/palette-animation/palette-animation.component";
73 import { CompositionGraphZoneUtils } from "./utils/composition-graph-zone-utils";
74 import {LeftPaletteMetadataTypes} from "../../../models/components/displayComponent";
77 export interface ICompositionGraphScope extends ng.IScope {
87 activeZoneInstance:ZoneInstanceConfig;
89 zoneInstanceModeChanged(newMode:ZoneInstanceMode, instance:ZoneInstanceConfig, zoneId:string);
90 clickOutsideZoneInstance():void;
92 // Link menu - create link menu
93 relationMenuDirectiveObj:ConnectRelationModel;
94 isLinkMenuOpen:boolean;
95 createLinkFromMenu:(chosenMatch:Match, vl:Component)=>void;
96 saveChangedCapabilityProperties:()=>Promise<PropertyBEModel[]>;
98 //modify link menu - for now only delete menu
99 relationMenuTimeout:ng.IPromise<any>;
100 linkMenuObject:LinkMenu;
103 //left palette functions callbacks
104 dropCallback(event:JQueryEventObject, ui:any):void;
105 beforeDropCallback(event:IDragDropEvent):void;
106 verifyDrop(event:JQueryEventObject, ui:any):void;
109 viewRelation(link:Cy.CollectionEdges):void;
110 deleteRelation(link:Cy.CollectionEdges):void;
113 //search,zoom in/out/all
114 componentInstanceNames: Array<string>; //id, name
115 zoom(zoomIn: boolean): void;
116 zoomAll(nodes?:Cy.CollectionNodes): void;
117 getAutoCompleteValues(searchTerm: string):void;
118 highlightSearchMatches(searchTerm: string): void;
122 createOrUpdateServicePath(data: any):void;
123 deletePathsOnCy():void;
124 drawPathOnCy(data: ForwardingPath):void;
125 selectedPathId: string;
127 /*//asset popover menu
128 assetPopoverObj:AssetPopoverObj;
129 assetPopoverOpen:boolean;
130 hideAssetPopover():void;
131 deleteNode(nodeId:string):void;*/
134 export class CompositionGraph implements ng.IDirective {
135 private _cy:Cy.Instance;
136 private _currentlyCLickedNodePosition:Cy.Position;
137 // private $document:JQuery = $(document);
138 private dragElement:JQuery;
139 private dragComponent:ComponentInstance;
141 constructor(private $q:ng.IQService,
142 private $log:ng.ILogService,
143 private $timeout:ng.ITimeoutService,
144 private NodesFactory:NodesFactory,
145 private CompositionGraphLinkUtils:CompositionGraphLinkUtils,
146 private GeneralGraphUtils:CompositionGraphGeneralUtils,
147 private ComponentInstanceFactory:ComponentInstanceFactory,
148 private NodesGraphUtils:CompositionGraphNodesUtils,
149 private eventListenerService:EventListenerService,
150 private ComponentFactory:ComponentFactory,
151 private LoaderService:LoaderService,
152 private commonGraphUtils:CommonGraphUtils,
153 private matchCapabilitiesRequirementsUtils:MatchCapabilitiesRequirementsUtils,
154 private CompositionGraphPaletteUtils:CompositionGraphPaletteUtils,
155 private compositionGraphZoneUtils:CompositionGraphZoneUtils,
156 private ComponentServiceNg2: ComponentServiceNg2,
157 private ModalServiceNg2: ModalService,
158 private ConnectionWizardServiceNg2: ConnectionWizardService,
159 private ComponentInstanceServiceNg2: ComponentInstanceServiceNg2,
160 private servicePathGraphUtils: ServicePathGraphUtils,
161 private policiesService:PoliciesService) {
166 template = require('./composition-graph.html');
173 link = (scope:ICompositionGraphScope, el:JQuery) => {
174 this.loadGraph(scope, el);
176 if(scope.component.componentInstances && scope.component.componentInstancesRelations) {
177 this.loadGraphData(scope);
179 //when we don't have the data we register to on graph load event
180 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED, () => {
181 this.loadGraphData(scope);
184 scope.$on('$destroy', () => {
186 _.forEach(GRAPH_EVENTS, (event) => {
187 this.eventListenerService.unRegisterObserver(event);
189 this.eventListenerService.unRegisterObserver(EVENTS.SHOW_LOADER_EVENT + 'composition-graph');
190 this.eventListenerService.unRegisterObserver(EVENTS.HIDE_LOADER_EVENT + 'composition-graph');
195 private loadGraphData = (scope:ICompositionGraphScope) => {
196 this.initGraphNodes(scope.component.componentInstances, scope.isViewOnly);
197 this.commonGraphUtils.initGraphLinks(this._cy, scope.component.componentInstancesRelations, scope.component.getRelationRequirementCapability.bind(scope.component));
198 this.commonGraphUtils.initUcpeChildren(this._cy);
199 this.compositionGraphZoneUtils.initPolicyInstances(scope.zones.policy, scope.component.policies);
202 private loadGraph = (scope:ICompositionGraphScope, el:JQuery) => {
203 let graphEl = el.find('.sdc-composition-graph-wrapper');
204 this.initGraph(graphEl, scope.isViewOnly);
205 this.initDropZone(scope);
206 this.initZones(scope);
207 this.registerCytoscapeGraphEvents(scope);
208 this.registerCustomEvents(scope, el);
209 this.initViewMode(scope.isViewOnly);
213 private initGraph(graphEl:JQuery, isViewOnly:boolean) {
215 this._cy = cytoscape({
217 style: ComponentInstanceNodesStyle.getCompositionGraphStyle(),
218 zoomingEnabled: true,
221 userZoomingEnabled: false,
222 userPanningEnabled: true,
223 selectionType: 'single',
224 boxSelectionEnabled: true,
225 autolock: isViewOnly,
226 autoungrabify: isViewOnly
230 private initViewMode(isViewOnly:boolean) {
233 //remove event listeners
234 this._cy.off('drag');
235 this._cy.off('handlemouseout');
236 this._cy.off('handlemouseover');
237 this._cy.edges().unselectify();
241 private registerCustomEvents(scope:ICompositionGraphScope, el:JQuery) {
243 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_IN, (leftPaletteComponent:LeftPaletteComponent) => {
245 leftPaletteComponent.categoryType === LeftPaletteMetadataTypes.Group ||
246 leftPaletteComponent.categoryType === LeftPaletteMetadataTypes.Policy) {
250 this.$log.info(`composition-graph::registerEventServiceEvents:: palette hover on component: ${leftPaletteComponent.uniqueId}`);
252 let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes());
253 let nodesLinks = this.GeneralGraphUtils.getAllCompositionCiLinks(this._cy);
255 if (this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.containsKey(leftPaletteComponent.uniqueId)) {
256 let cacheComponent = this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.getValue(leftPaletteComponent.uniqueId);
257 let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findMatchingNodes(cacheComponent, nodesData, nodesLinks);
259 this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
260 this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy);
265 //----------------------- ORIT TO FIX------------------------//
267 this.ComponentServiceNg2.getCapabilitiesAndRequirements(leftPaletteComponent.componentType, leftPaletteComponent.uniqueId).subscribe((response: ComponentGenericResponse) => {
269 let component = this.ComponentFactory.createEmptyComponent(leftPaletteComponent.componentType);
270 component.uniqueId = component.uniqueId;
271 component.capabilities = response.capabilities;
272 component.requirements = response.requirements;
273 this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.setValue(leftPaletteComponent.uniqueId, component);
274 let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findMatchingNodes(component, nodesData, nodesLinks);
275 this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy);
276 this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy)
280 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ADD_COMPONENT_INSTANCE_ZONE_START, (component:Component, paletteComponent:LeftPaletteComponent, startPosition:Point) => {
281 this.LoaderService.showLoader('composition-graph');
283 let zoneType:string = LeftPaletteMetadataTypes[paletteComponent.categoryType].toLowerCase();
284 scope.zones[zoneType].showZone = true;
285 if(scope.minifyZone) scope.minifyZone = false;
287 this.policiesService.createPolicyInstance(component.componentType, component.uniqueId, paletteComponent.type).subscribe((newInstance)=>{
289 this.LoaderService.hideLoader('composition-graph');
290 scope.newZoneInstance = newInstance;
291 this.compositionGraphZoneUtils.showAnimationToZone(startPosition, zoneType);
293 this.LoaderService.hideLoader('composition-graph');
297 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_FINISH_ANIMATION_ZONE, () => {
298 if(scope.newZoneInstance){
299 this.compositionGraphZoneUtils.addInstanceToZone(scope.zones['policy'], scope.newZoneInstance);
303 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ZONE_SIZE_CHANGE, () => {
304 scope.minifyZone = true;
307 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_OUT, () => {
308 this._cy.emit('hidehandles');
309 this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
312 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_START, (dragElement, dragComponent) => {
314 this.dragElement = dragElement;
315 this.dragComponent = this.ComponentInstanceFactory.createComponentInstanceFromComponent(dragComponent);
318 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_ACTION, (event:IDragDropEvent) => {
319 this.CompositionGraphPaletteUtils.onComponentDrag(this._cy, event, this.dragElement, this.dragComponent);
323 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, (component:ComponentInstance) => {
325 let selectedNode = this._cy.getElementById(component.uniqueId);
326 selectedNode.data().componentInstance.name = component.name;
327 selectedNode.data('name', component.name); //used for tooltip
328 selectedNode.data('displayName', selectedNode.data().getDisplayName()); //abbreviated
332 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, (componentInstance:ComponentInstance) => {
333 let nodeToDelete = this._cy.getElementById(componentInstance.uniqueId);
334 this.NodesGraphUtils.deleteNode(this._cy, scope.component, nodeToDelete);
337 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_MULTIPLE_COMPONENTS, () => {
339 this._cy.$('node:selected').each((i:number, node:Cy.CollectionNodes) => {
340 this.NodesGraphUtils.deleteNode(this._cy, scope.component, node);
345 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_EDGE, (releaseLoading:boolean, linksToDelete:Cy.CollectionEdges) => {
346 this.CompositionGraphLinkUtils.deleteLink(this._cy, scope.component, releaseLoading, linksToDelete);
349 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_INSERT_NODE_TO_UCPE, (node:Cy.CollectionNodes, ucpe:Cy.CollectionNodes, updateExistingNode:boolean) => {
351 this.commonGraphUtils.initUcpeChildData(node, ucpe);
352 //check if item is a VL, and if so, skip adding the binding to ucpe
353 if (!(node.data() instanceof CompositionCiNodeVl)) {
354 this.CompositionGraphLinkUtils.createVfToUcpeLink(scope.component, this._cy, ucpe.data(), node.data()); //create link from the node to the ucpe
357 if (updateExistingNode) {
358 let vlsPendingDeletion:Cy.CollectionNodes = this.NodesGraphUtils.deleteNodeVLsUponMoveToOrFromUCPE(scope.component, node.cy(), node); //delete connected VLs that no longer have 2 links
359 this.CompositionGraphLinkUtils.deleteLinksWhenNodeMovedFromOrToUCPE(scope.component, node.cy(), node, vlsPendingDeletion); //delete all connected links if needed
360 this.GeneralGraphUtils.pushUpdateComponentInstanceActionToQueue(scope.component, true, node.data().componentInstance); //update componentInstance position
365 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_REMOVE_NODE_FROM_UCPE, (node:Cy.CollectionNodes, ucpe:Cy.CollectionNodes) => {
366 this.commonGraphUtils.removeUcpeChildData(node);
367 let vlsPendingDeletion:Cy.CollectionNodes = this.NodesGraphUtils.deleteNodeVLsUponMoveToOrFromUCPE(scope.component, node.cy(), node);
368 this.CompositionGraphLinkUtils.deleteLinksWhenNodeMovedFromOrToUCPE(scope.component, node.cy(), node, vlsPendingDeletion); //delete all connected links if needed
369 this.GeneralGraphUtils.pushUpdateComponentInstanceActionToQueue(scope.component, true, node.data().componentInstance); //update componentInstance position
372 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_VERSION_CHANGED, (component:Component) => {
373 scope.component = component;
374 this._cy.elements().remove();
375 this.loadGraphData(scope);
378 scope.zoom = (zoomIn: boolean):void => {
379 let currentZoom: number = this._cy.zoom();
381 this.GeneralGraphUtils.zoomGraphTo(this._cy, currentZoom + .1);
383 this.GeneralGraphUtils.zoomGraphTo(this._cy, currentZoom - .1);
387 //Zooms to fit all of the nodes in the collection passed in. If no nodes are passed in, will zoom to fit all nodes on graph
388 scope.zoomAll = (nodes?:Cy.CollectionNodes) => {
389 if (!nodes || !nodes.length) {
390 nodes = this._cy.nodes();
393 scope.withSidebar = false;
395 fit: { eles: nodes, padding: 20 },
396 center: { eles: nodes }
397 }, { duration: 400 });
400 scope.getAutoCompleteValues = (searchTerm: string) => {
401 if (searchTerm.length > 1) { //US requirement: only display search results after 2nd letter typed.
402 let nodes: Cy.CollectionNodes = this.NodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
403 scope.componentInstanceNames = _.map(nodes, node => node.data('name'));
405 scope.componentInstanceNames = [];
409 scope.highlightSearchMatches = (searchTerm: string) => {
410 this.NodesGraphUtils.highlightMatchingNodesByName(this._cy, searchTerm);
411 let matchingNodes: Cy.CollectionNodes = this.NodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
412 scope.zoomAll(matchingNodes);
415 scope.saveChangedCapabilityProperties = ():Promise<PropertyBEModel[]> => {
416 return new Promise<PropertyBEModel[]>((resolve) => {
417 const capabilityPropertiesBE: PropertyBEModel[] = this.ConnectionWizardServiceNg2.changedCapabilityProperties.map((prop) => {
418 prop.value = prop.getJSONValue();
419 const propBE = new PropertyBEModel(prop);
420 propBE.parentUniqueId = this.ConnectionWizardServiceNg2.selectedMatch.relationship.relation.capabilityOwnerId;
423 if (capabilityPropertiesBE.length > 0) {
424 // if there are capability properties to update, then first update capability properties and then resolve promise
425 this.ComponentInstanceServiceNg2
426 .updateInstanceCapabilityProperties(
428 this.ConnectionWizardServiceNg2.selectedMatch.toNode,
429 this.ConnectionWizardServiceNg2.selectedMatch.capability,
430 capabilityPropertiesBE
432 .subscribe((response) => {
433 console.log("Update resource instance capability properties response: ", response);
434 this.ConnectionWizardServiceNg2.changedCapabilityProperties = [];
435 resolve(capabilityPropertiesBE);
438 // no capability properties to update, immediately resolve promise
439 resolve(capabilityPropertiesBE);
444 scope.createLinkFromMenu = ():void => {
445 scope.isLinkMenuOpen = false;
447 scope.saveChangedCapabilityProperties().then(() => {
449 this.CompositionGraphLinkUtils
450 .createLinkFromMenu(this._cy, this.ConnectionWizardServiceNg2.selectedMatch, scope.component);
454 scope.hideRelationMenu = () => {
455 this.commonGraphUtils.safeApply(scope, () => {
456 delete scope.canvasMenuProps;
457 this.$timeout.cancel(scope.relationMenuTimeout);
461 scope.createOrUpdateServicePath = (data:any) => {
462 this.servicePathGraphUtils.createOrUpdateServicePath(scope, data);
464 scope.deletePathsOnCy = () => {
465 this.servicePathGraphUtils.deletePathsFromGraph(this._cy, <Service> scope.component);
467 scope.drawPathOnCy = (data: ForwardingPath) => {
468 this.servicePathGraphUtils.drawPath(this._cy, data, <Service> scope.component);
471 scope.viewRelation = (link:Cy.CollectionEdges) => {
472 scope.hideRelationMenu();
474 const linkData = link.data();
475 const sourceNode:CompositionCiNodeBase = link.source().data();
476 const targetNode:CompositionCiNodeBase = link.target().data();
477 const relationship:Relationship = linkData.relation.relationships[0];
479 scope.component.getRelationRequirementCapability(relationship, sourceNode.componentInstance, targetNode.componentInstance).then((objReqCap) => {
480 const capability = objReqCap.capability;
481 const requirement = objReqCap.requirement;
483 this.ConnectionWizardServiceNg2.currentComponent = scope.component;
484 this.ConnectionWizardServiceNg2.connectRelationModel = new ConnectRelationModel(sourceNode, targetNode, []);
485 this.ConnectionWizardServiceNg2.selectedMatch = new Match(requirement, capability, true, linkData.source, linkData.target);
486 this.ConnectionWizardServiceNg2.selectedMatch.relationship = relationship;
488 const title = `Connection Properties`;
489 const saveButton: ButtonModel = new ButtonModel('Save', 'blue', () => {
490 scope.saveChangedCapabilityProperties().then(() => { this.ModalServiceNg2.closeCurrentModal(); })
492 const cancelButton: ButtonModel = new ButtonModel('Cancel', 'white', () => { this.ModalServiceNg2.closeCurrentModal(); });
493 const modal = new ModalModel('xl', title, '', [saveButton, cancelButton]);
494 const modalInstance = this.ModalServiceNg2.createCustomModal(modal);
495 this.ModalServiceNg2.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent);
496 modalInstance.instance.open();
498 new Promise((resolve) => {
499 if (!this.ConnectionWizardServiceNg2.selectedMatch.capability.properties) {
500 this.ComponentInstanceServiceNg2.getInstanceCapabilityProperties(scope.component, linkData.target, capability)
508 this.ModalServiceNg2.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent);
514 scope.deleteRelation = (link:Cy.CollectionEdges) => {
515 scope.hideRelationMenu();
517 //if multiple edges selected, delete the VL itself so edges get deleted automatically
518 if (this._cy.$('edge:selected').length > 1) {
519 this.NodesGraphUtils.deleteNode(this._cy, scope.component, this._cy.$('node:selected'));
521 this.CompositionGraphLinkUtils.deleteLink(this._cy, scope.component, true, link);
526 scope.hideAssetPopover = ():void => {
528 this.commonGraphUtils.safeApply(scope, () => {
529 scope.assetPopoverOpen = false;
530 scope.assetPopoverObj = null;
534 scope.deleteNode = (nodeId:string):void => {
535 if (!scope.isViewOnly) {
536 this.NodesGraphUtils.confirmDeleteNode(nodeId, this._cy, scope.component);
537 //scope.hideAssetPopover();
542 private registerCytoscapeGraphEvents(scope:ICompositionGraphScope) {
544 this._cy.on('addedgemouseup', (event, data) => {
545 scope.relationMenuDirectiveObj = this.CompositionGraphLinkUtils.onLinkDrawn(this._cy, data.source, data.target);
546 if (scope.relationMenuDirectiveObj != null) {
547 this.ConnectionWizardServiceNg2.setRelationMenuDirectiveObj(scope.relationMenuDirectiveObj);
548 this.ConnectionWizardServiceNg2.currentComponent = scope.component;
549 //TODO: init with the selected values
550 this.ConnectionWizardServiceNg2.selectedMatch = null;
552 let steps:Array<StepModel> = [];
553 let fromNodeName:string = scope.relationMenuDirectiveObj.fromNode.componentInstance.name;
554 let toNodeName:string = scope.relationMenuDirectiveObj.toNode.componentInstance.name;
555 steps.push(new StepModel(fromNodeName, FromNodeStepComponent));
556 steps.push(new StepModel(toNodeName, ToNodeStepComponent));
557 steps.push(new StepModel('Properties', PropertiesStepComponent));
558 let wizardTitle = 'Connect: ' + fromNodeName + ' to ' + toNodeName;
559 let modalInstance = this.ModalServiceNg2.createMultiStepsWizard(wizardTitle, steps, scope.createLinkFromMenu, ConnectionWizardHeaderComponent);
560 modalInstance.instance.open();
563 // this.ModalServiceNg2.createMultiStepsWizard('Connect', )Connect
564 // scope.$apply(() => {
565 // scope.isLinkMenuOpen = true;
569 this._cy.on('tapstart', 'node', (event:Cy.EventObject) => {
570 scope.isOnDrag = true;
571 this._currentlyCLickedNodePosition = angular.copy(event.cyTarget[0].position()); //update node position on drag
572 if (event.cyTarget.data().isUcpe) {
573 this._cy.nodes('.ucpe-cp').unlock();
574 event.cyTarget.style('opacity', 0.5);
576 //scope.hideAssetPopover();
579 this._cy.on('drag', 'node', (event:Cy.EventObject) => {
581 if (event.cyTarget.data().isDraggable) {
582 event.cyTarget.style({'overlay-opacity': 0.24});
583 if (this.GeneralGraphUtils.isValidDrop(this._cy, event.cyTarget)) {
584 event.cyTarget.style({'overlay-color': GraphColors.NODE_BACKGROUND_COLOR});
586 event.cyTarget.style({'overlay-color': GraphColors.NODE_OVERLAPPING_BACKGROUND_COLOR});
590 if (event.cyTarget.data().isUcpe) {
591 let pos = event.cyTarget.position();
593 this._cy.nodes('[?isInsideGroup]').positions((i, node)=> {
595 x: pos.x + node.data("ucpeOffset").x,
596 y: pos.y + node.data("ucpeOffset").y
602 /* this._cy.on('mouseover', 'node', (event:Cy.EventObject) => {
603 if (!this._cy.scratch('_edge_editation_highlights')) {
604 this.commonGraphUtils.safeApply(scope, () => {
605 this.showNodePopoverMenu(scope, event.cyTarget[0]);
610 this._cy.on('mouseout', 'node', (event:Cy.EventObject) => {
611 scope.hideAssetPopover();
613 this._cy.on('handlemouseover', (event, payload) => {
615 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
618 let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes());
619 let nodesLinks = this.GeneralGraphUtils.getAllCompositionCiLinks(this._cy);
621 let linkableNodes = this.commonGraphUtils.getLinkableNodes(this._cy, payload.node);
622 let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findMatchingNodes(payload.node.data().componentInstance, linkableNodes, nodesLinks);
623 this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
624 this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy, payload.node.data());
626 this._cy.scratch()._edge_editation_highlights = true;
627 /*scope.hideAssetPopover();*/
630 this._cy.on('handlemouseout', () => {
631 if (this._cy.scratch('_edge_editation_highlights') === true) {
632 this._cy.removeScratch('_edge_editation_highlights');
633 this._cy.emit('hidehandles');
634 this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
639 this._cy.on('tapend', (event:Cy.EventObject) => {
640 scope.isOnDrag = false;
641 if (event.cyTarget === this._cy) { //On Background clicked
642 if (this._cy.$('node:selected').length === 0) { //if the background click but not dragged
643 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED);
645 scope.hideRelationMenu();
648 else if (event.cyTarget.isEdge()) { //On Edge clicked
649 if (scope.isViewOnly) return;
650 this.CompositionGraphLinkUtils.handleLinkClick(this._cy, event);
651 if (event.cyTarget.data().type === CompositionCiServicePathLink.LINK_TYPE) {
654 this.openModifyLinkMenu(scope, this.CompositionGraphLinkUtils.getModifyLinkMenu(event.cyTarget[0], event), 6000);
657 else { //On Node clicked
658 this._cy.nodes(':grabbed').style({'overlay-opacity': 0});
660 let isUcpe:boolean = event.cyTarget.data().isUcpe;
661 let newPosition = event.cyTarget[0].position();
662 //node position changed (drop after drag event) - we need to update position
663 if (this._currentlyCLickedNodePosition.x !== newPosition.x || this._currentlyCLickedNodePosition.y !== newPosition.y) {
664 let nodesMoved:Cy.CollectionNodes = this._cy.$(':grabbed');
666 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.
668 this.NodesGraphUtils.onNodesPositionChanged(this._cy, scope.component, nodesMoved);
670 this.$log.debug('composition-graph::onNodeSelectedEvent:: fired');
672 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_NODE_SELECTED, event.cyTarget.data().componentInstance);
673 //open node popover menu
674 //this.showNodePopoverMenu(scope, event.cyTarget[0]);
679 this._cy.nodes('.ucpe-cp').lock();
680 event.cyTarget.style('opacity', 1);
686 this._cy.on('boxselect', 'node', (event:Cy.EventObject) => {
687 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_NODE_SELECTED, event.cyTarget.data().componentInstance);
691 private openModifyLinkMenu = (scope:ICompositionGraphScope, linkMenuObject:LinkMenu, timeOutInMilliseconds?:number) => {
692 scope.hideRelationMenu();
693 this.$timeout(() => {
694 scope.canvasMenuProps = {
696 styleClass: 'w-sdc-canvas-menu-list',
699 x: `${linkMenuObject.position.x}px`,
700 y: `${linkMenuObject.position.y}px`
704 if (this._cy.$('edge:selected').length === 1) {
705 scope.canvasMenuProps.items.push({
707 styleClass: 'w-sdc-canvas-menu-item-view',
709 scope.viewRelation(<Cy.CollectionEdges>linkMenuObject.link);
713 scope.canvasMenuProps.items.push({
715 styleClass: 'w-sdc-canvas-menu-item-delete',
717 scope.deleteRelation(<Cy.CollectionEdges>linkMenuObject.link);
721 scope.relationMenuTimeout = this.$timeout(() => {
722 scope.hideRelationMenu();
723 }, timeOutInMilliseconds ? timeOutInMilliseconds : 6000);
727 private initGraphNodes(componentInstances:ComponentInstance[], isViewOnly:boolean) {
729 if (!isViewOnly) { //Init nodes handle extension - enable dynamic links
731 let handles = new CytoscapeEdgeEditation;
732 handles.init(this._cy, 18);
733 handles.registerHandle(ComponentInstanceNodesStyle.getBasicNodeHanlde());
734 handles.registerHandle(ComponentInstanceNodesStyle.getBasicSmallNodeHandle());
735 handles.registerHandle(ComponentInstanceNodesStyle.getUcpeCpNodeHandle());
739 _.each(componentInstances, (instance) => {
740 let compositionGraphNode:CompositionCiNodeBase = this.NodesFactory.createNode(instance);
741 this.commonGraphUtils.addComponentInstanceNodeToGraph(this._cy, compositionGraphNode);
746 private initDropZone(scope:ICompositionGraphScope) {
748 if (scope.isViewOnly) {
751 scope.dropCallback = (event:IDragDropEvent) => {
752 this.$log.debug(`composition-graph::dropCallback:: fired`);
753 this.CompositionGraphPaletteUtils.addNodeFromPalette(this._cy, event, scope.component);
756 scope.verifyDrop = (event:JQueryEventObject) => {
758 if (this.dragElement.hasClass('red')) {
764 scope.beforeDropCallback = (event:IDragDropEvent):ng.IPromise<void> => {
765 let deferred:ng.IDeferred<void> = this.$q.defer<void>();
766 if (this.dragElement.hasClass('red')) {
772 return deferred.promise;
777 private initZones = (scope:ICompositionGraphScope):void => {
778 scope.zones = this.compositionGraphZoneUtils.createCompositionZones();
780 scope.zoneInstanceModeChanged = (newMode:ZoneInstanceMode, instance:ZoneInstanceConfig, zoneId:string):void => {
781 if(scope.zoneTagMode) { //we're in tag mode.
782 if(instance == scope.activeZoneInstance && newMode == ZoneInstanceMode.TAG){ //we want to toggle tag mode off.
783 scope.unsetActiveZoneInstance();
786 scope.setZoneInstanceMode(newMode, instance, zoneId);
790 scope.setZoneInstanceMode = (newMode:ZoneInstanceMode, instance:ZoneInstanceConfig, zoneId:string):void => {
791 instance.mode = newMode;
793 case ZoneInstanceMode.TAG: {
794 scope.zoneTagMode = zoneId + "-tagging";
796 case ZoneInstanceMode.SELECTED: { //case TAG flows into here as well
797 scope.activeZoneInstance = instance;
803 scope.unsetActiveZoneInstance = ():void => {
804 scope.activeZoneInstance.mode = ZoneInstanceMode.NONE;
805 scope.activeZoneInstance = null;
806 scope.zoneTagMode = null;
809 scope.clickOutsideZoneInstance = ():void => {
810 if(!scope.zoneTagMode)
811 scope.unsetActiveZoneInstance();
819 public static factory = ($q,
825 ComponentInstanceFactory,
827 EventListenerService,
831 MatchCapabilitiesRequirementsUtils,
832 CompositionGraphPaletteUtils,
833 CompositionGraphZoneUtils,
836 ConnectionWizardService,
837 ComponentInstanceServiceNg2,
838 ServicePathGraphUtils,
839 PoliciesService) => {
840 return new CompositionGraph(
847 ComponentInstanceFactory,
849 EventListenerService,
853 MatchCapabilitiesRequirementsUtils,
854 CompositionGraphPaletteUtils,
855 CompositionGraphZoneUtils,
858 ConnectionWizardService,
859 ComponentInstanceServiceNg2,
860 ServicePathGraphUtils,
865 CompositionGraph.factory.$inject = [
870 'CompositionGraphLinkUtils',
871 'CompositionGraphGeneralUtils',
872 'ComponentInstanceFactory',
873 'CompositionGraphNodesUtils',
874 'EventListenerService',
878 'MatchCapabilitiesRequirementsUtils',
879 'CompositionGraphPaletteUtils',
880 'CompositionGraphZoneUtils',
881 'ComponentServiceNg2',
883 'ConnectionWizardServiceNg2',
884 'ComponentInstanceServiceNg2',
885 'ServicePathGraphUtils',