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, DEPENDENCY_EVENTS } 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 zoneBackgroundClicked() :void;
91 zoneInstanceModeChanged(newMode: ZoneInstanceMode, instance: ZoneInstance, zoneId: ZoneInstanceType);
92 unsetActiveZoneInstance(): void;
93 clickOutsideZoneInstance(): void;
94 zoneAssignmentSaveStart(): void;
95 zoneAssignmentSaveComplete(success: boolean): void;
97 // Link menu - create link menu
98 relationMenuDirectiveObj: ConnectRelationModel;
99 isLinkMenuOpen: boolean;
100 createLinkFromMenu: (chosenMatch: Match, vl: Component) => void;
101 saveChangedCapabilityProperties: () => Promise<PropertyBEModel[]>;
103 //modify link menu - for now only delete menu
104 relationMenuTimeout: ng.IPromise<any>;
105 linkMenuObject: LinkMenu;
108 //left palette functions callbacks
109 dropCallback(event: JQueryEventObject, ui: any): void;
110 beforeDropCallback(event: IDragDropEvent): void;
111 verifyDrop(event: JQueryEventObject, ui: any): void;
114 viewRelation(link: Cy.CollectionEdges): void;
115 deleteRelation(link: Cy.CollectionEdges): void;
118 //search,zoom in/out/all
119 componentInstanceNames: Array<string>; //id, name
120 zoom(zoomIn: boolean): void;
121 zoomAllWithoutSidebar(): void;
122 getAutoCompleteValues(searchTerm: string): void;
123 highlightSearchMatches(searchTerm: string): void;
125 canvasMenuProps: any;
127 createOrUpdateServicePath(data: any): void;
128 deletePathsOnCy(): void;
129 drawPathOnCy(data: ForwardingPath): void;
130 selectedPathId: string;
133 export class CompositionGraph implements ng.IDirective {
134 private _cy: Cy.Instance;
135 private _currentlyCLickedNodePosition: Cy.Position;
136 private dragElement: JQuery;
137 private dragComponent: ComponentInstance;
139 constructor(private $q: ng.IQService,
140 private $log: ng.ILogService,
141 private $timeout: ng.ITimeoutService,
142 private NodesFactory: NodesFactory,
143 private CompositionGraphLinkUtils: CompositionGraphLinkUtils,
144 private GeneralGraphUtils: CompositionGraphGeneralUtils,
145 private ComponentInstanceFactory: ComponentInstanceFactory,
146 private NodesGraphUtils: CompositionGraphNodesUtils,
147 private eventListenerService: EventListenerService,
148 private ComponentFactory: ComponentFactory,
149 private LoaderService: LoaderService,
150 private commonGraphUtils: CommonGraphUtils,
151 private matchCapabilitiesRequirementsUtils: MatchCapabilitiesRequirementsUtils,
152 private CompositionGraphPaletteUtils: CompositionGraphPaletteUtils,
153 private compositionGraphZoneUtils: CompositionGraphZoneUtils,
154 private ComponentServiceNg2: ComponentServiceNg2,
155 private ModalServiceNg2: ModalService,
156 private ConnectionWizardServiceNg2: ConnectionWizardService,
157 private ComponentInstanceServiceNg2: ComponentInstanceServiceNg2,
158 private servicePathGraphUtils: ServicePathGraphUtils) {
163 template = require('./composition-graph.html');
170 link = (scope: ICompositionGraphScope, el: JQuery) => {
171 this.loadGraph(scope, el);
173 if (!scope.component.groupInstances || !scope.component.policies) {
174 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED, () => {
175 this.loadGraphData(scope);
178 this.loadGraphData(scope);
182 scope.$on('$destroy', () => {
184 _.forEach(GRAPH_EVENTS, (event) => {
185 this.eventListenerService.unRegisterObserver(event);
187 this.eventListenerService.unRegisterObserver(EVENTS.SHOW_LOADER_EVENT + 'composition-graph');
188 this.eventListenerService.unRegisterObserver(EVENTS.HIDE_LOADER_EVENT + 'composition-graph');
189 this.eventListenerService.unRegisterObserver(DEPENDENCY_EVENTS.ON_DEPENDENCY_CHANGE);
194 private loadGraphData = (scope: ICompositionGraphScope) => {
195 this.initGraphNodes(scope.component.componentInstances, scope.isViewOnly);
196 this.commonGraphUtils.initGraphLinks(this._cy, scope.component.componentInstancesRelations, scope.component.getRelationRequirementCapability.bind(scope.component));
197 this.commonGraphUtils.initUcpeChildren(this._cy);
198 this.compositionGraphZoneUtils.initZoneInstances(scope.zones, scope.component);
199 setTimeout(() => {//Need settimeout so that angular canvas changes will take effect before resize & center
200 this.GeneralGraphUtils.zoomAllWithMax(this._cy, 1);
204 private loadGraph = (scope: ICompositionGraphScope, el: JQuery) => {
205 let graphEl = el.find('.sdc-composition-graph-wrapper');
206 this.initGraph(graphEl, scope.isViewOnly);
207 this.initDropZone(scope);
208 this.initZones(scope);
209 this.registerCytoscapeGraphEvents(scope);
210 this.registerCustomEvents(scope, el);
211 this.initViewMode(scope.isViewOnly);
214 private initGraph(graphEl: JQuery, isViewOnly: boolean) {
216 this._cy = cytoscape({
218 style: ComponentInstanceNodesStyle.getCompositionGraphStyle(),
219 zoomingEnabled: true,
222 userZoomingEnabled: false,
223 userPanningEnabled: true,
224 selectionType: 'single',
225 boxSelectionEnabled: true,
226 autolock: isViewOnly,
227 autoungrabify: isViewOnly
231 private initViewMode(isViewOnly: boolean) {
234 //remove event listeners
235 this._cy.off('drag');
236 this._cy.off('handlemouseout');
237 this._cy.off('handlemouseover');
238 this._cy.off('canvasredraw');
239 this._cy.off('handletagclick')
240 this._cy.edges().unselectify();
244 private registerCustomEvents(scope: ICompositionGraphScope, el: JQuery) {
246 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_GROUP_INSTANCE_UPDATE, (groupInstance: GroupInstance) => {
247 this.compositionGraphZoneUtils.findAndUpdateZoneInstanceData(scope.zones, groupInstance);
248 this.GeneralGraphUtils.showGroupUpdateSuccess();
251 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_POLICY_INSTANCE_UPDATE, (policyInstance: PolicyInstance) => {
252 this.compositionGraphZoneUtils.findAndUpdateZoneInstanceData(scope.zones, policyInstance);
253 this.GeneralGraphUtils.showPolicyUpdateSuccess();
256 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_IN, (leftPaletteComponent: LeftPaletteComponent) => {
257 if (scope.isOnDrag) {
261 this.$log.info(`composition-graph::registerEventServiceEvents:: palette hover on component: ${leftPaletteComponent.uniqueId}`);
263 let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes());
264 let nodesLinks = this.GeneralGraphUtils.getAllCompositionCiLinks(this._cy);
266 if (this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.containsKey(leftPaletteComponent.uniqueId)) {
267 let cacheComponent = this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.getValue(leftPaletteComponent.uniqueId);
268 let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findMatchingNodes(cacheComponent, nodesData, nodesLinks);
270 this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
271 this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy);
276 //----------------------- ORIT TO FIX------------------------//
278 this.ComponentServiceNg2.getCapabilitiesAndRequirements(leftPaletteComponent.componentType, leftPaletteComponent.uniqueId).subscribe((response: ComponentGenericResponse) => {
280 let component = this.ComponentFactory.createEmptyComponent(leftPaletteComponent.componentType);
281 component.uniqueId = component.uniqueId;
282 component.capabilities = response.capabilities;
283 component.requirements = response.requirements;
284 this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.setValue(leftPaletteComponent.uniqueId, component);
288 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ADD_ZONE_INSTANCE_FROM_PALETTE, (component: Component, paletteComponent: LeftPaletteComponent, startPosition: Point) => {
290 let zoneType: ZoneInstanceType = this.compositionGraphZoneUtils.getZoneTypeForPaletteComponent(paletteComponent.categoryType);
291 this.compositionGraphZoneUtils.showZone(scope.zones[zoneType]);
293 this.LoaderService.showLoader('composition-graph');
294 this.compositionGraphZoneUtils.createZoneInstanceFromLeftPalette(zoneType, component, paletteComponent.type).subscribe((zoneInstance: ZoneInstance) => {
295 this.LoaderService.hideLoader('composition-graph');
296 this.compositionGraphZoneUtils.addInstanceToZone(scope.zones[zoneInstance.type], zoneInstance, true);
297 this.compositionGraphZoneUtils.createPaletteToZoneAnimation(startPosition, zoneType, zoneInstance);
299 this.LoaderService.hideLoader('composition-graph');
303 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_OUT, () => {
305 this._cy.emit('hidehandles');
306 this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
309 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_START, (dragElement, dragComponent) => {
311 this.dragElement = dragElement;
312 this.dragComponent = this.ComponentInstanceFactory.createComponentInstanceFromComponent(dragComponent);
315 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_ACTION, (event: IDragDropEvent) => {
316 this.CompositionGraphPaletteUtils.onComponentDrag(this._cy, event, this.dragElement, this.dragComponent);
320 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, (component: ComponentInstance) => {
322 let selectedNode = this._cy.getElementById(component.uniqueId);
323 selectedNode.data().componentInstance.name = component.name;
324 selectedNode.data('name', component.name); //used for tooltip
325 selectedNode.data('displayName', selectedNode.data().getDisplayName()); //abbreviated
329 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, (componentInstance: ComponentInstance) => {
330 let nodeToDelete = this._cy.getElementById(componentInstance.uniqueId);
331 this.NodesGraphUtils.deleteNode(this._cy, scope.component, nodeToDelete);
334 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_ZONE_INSTANCE, (deletedInstance: UIZoneInstanceObject) => {
336 if (deletedInstance.type === ZoneInstanceType.POLICY) {
337 scope.component.policies = scope.component.policies.filter(policy => policy.uniqueId !== deletedInstance.uniqueId);
338 } else if (deletedInstance.type === ZoneInstanceType.GROUP) {
339 scope.component.groupInstances = scope.component.groupInstances.filter(group => group.uniqueId !== deletedInstance.uniqueId);
341 //remove it from zones
342 scope.zones[deletedInstance.type].removeInstance(deletedInstance.uniqueId);
343 if (deletedInstance.type === ZoneInstanceType.GROUP && !_.isEmpty(scope.zones[ZoneInstanceType.POLICY])) {
344 this.compositionGraphZoneUtils.updateTargetsOrMembersOnCanvasDelete(deletedInstance.uniqueId, [scope.zones[ZoneInstanceType.POLICY]], ZoneInstanceAssignmentType.GROUPS);
346 this.eventListenerService.notifyObservers(EVENTS.UPDATE_PANEL);
349 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE_SUCCESS, (componentInstanceId: string) => {
350 if (!_.isEmpty(scope.zones)) {
351 this.compositionGraphZoneUtils.updateTargetsOrMembersOnCanvasDelete(componentInstanceId, scope.zones, ZoneInstanceAssignmentType.COMPONENT_INSTANCES);
355 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_EDGE, (releaseLoading: boolean, linksToDelete: Cy.CollectionEdges) => {
356 this.CompositionGraphLinkUtils.deleteLink(this._cy, scope.component, releaseLoading, linksToDelete);
359 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_INSERT_NODE_TO_UCPE, (node: Cy.CollectionNodes, ucpe: Cy.CollectionNodes, updateExistingNode: boolean) => {
361 this.commonGraphUtils.initUcpeChildData(node, ucpe);
362 //check if item is a VL, and if so, skip adding the binding to ucpe
363 if (!(node.data() instanceof CompositionCiNodeVl)) {
364 this.CompositionGraphLinkUtils.createVfToUcpeLink(scope.component, this._cy, ucpe.data(), node.data()); //create link from the node to the ucpe
367 if (updateExistingNode) {
368 let vlsPendingDeletion: Cy.CollectionNodes = this.NodesGraphUtils.deleteNodeVLsUponMoveToOrFromUCPE(scope.component, node.cy(), node); //delete connected VLs that no longer have 2 links
369 this.CompositionGraphLinkUtils.deleteLinksWhenNodeMovedFromOrToUCPE(scope.component, node.cy(), node, vlsPendingDeletion); //delete all connected links if needed
370 this.GeneralGraphUtils.pushUpdateComponentInstanceActionToQueue(scope.component, true, node.data().componentInstance); //update componentInstance position
375 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_REMOVE_NODE_FROM_UCPE, (node: Cy.CollectionNodes, ucpe: Cy.CollectionNodes) => {
376 this.commonGraphUtils.removeUcpeChildData(node);
377 let vlsPendingDeletion: Cy.CollectionNodes = this.NodesGraphUtils.deleteNodeVLsUponMoveToOrFromUCPE(scope.component, node.cy(), node);
378 this.CompositionGraphLinkUtils.deleteLinksWhenNodeMovedFromOrToUCPE(scope.component, node.cy(), node, vlsPendingDeletion); //delete all connected links if needed
379 this.GeneralGraphUtils.pushUpdateComponentInstanceActionToQueue(scope.component, true, node.data().componentInstance); //update componentInstance position
382 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_VERSION_CHANGED, (component: Component) => {
383 scope.component = component;
384 this._cy.elements().remove();
385 this.loadGraphData(scope);
388 this.eventListenerService.registerObserverCallback(DEPENDENCY_EVENTS.ON_DEPENDENCY_CHANGE, (ischecked: boolean) => {
390 this._cy.$('node:selected').addClass('dependent');
392 // due to defect in cytoscape, just changing the class does not replace the icon, and i need to revert to original icon with no markings.
393 this._cy.$('node:selected').removeClass('dependent');
394 this._cy.$('node:selected').style({'background-image': this._cy.$('node:selected').data('originalImg')});
398 scope.zoom = (zoomIn: boolean): void => {
399 let currentZoom: number = this._cy.zoom();
401 this.GeneralGraphUtils.zoomGraphTo(this._cy, currentZoom + .1);
403 this.GeneralGraphUtils.zoomGraphTo(this._cy, currentZoom - .1);
408 scope.zoomAllWithoutSidebar = () => {
409 scope.withSidebar = false;
410 setTimeout(() => { //wait for sidebar changes to take effect before zooming
411 this.GeneralGraphUtils.zoomAll(this._cy);
415 scope.getAutoCompleteValues = (searchTerm: string) => {
416 if (searchTerm.length > 1) { //US requirement: only display search results after 2nd letter typed.
417 let nodes: Cy.CollectionNodes = this.NodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
418 scope.componentInstanceNames = _.map(nodes, node => node.data('name'));
420 scope.componentInstanceNames = [];
424 scope.highlightSearchMatches = (searchTerm: string) => {
425 this.NodesGraphUtils.highlightMatchingNodesByName(this._cy, searchTerm);
426 let matchingNodes: Cy.CollectionNodes = this.NodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
427 this.GeneralGraphUtils.zoomAll(this._cy, matchingNodes);
430 scope.saveChangedCapabilityProperties = (): Promise<PropertyBEModel[]> => {
431 return new Promise<PropertyBEModel[]>((resolve) => {
432 const capabilityPropertiesBE: PropertyBEModel[] = this.ConnectionWizardServiceNg2.changedCapabilityProperties.map((prop) => {
433 prop.value = prop.getJSONValue();
434 const propBE = new PropertyBEModel(prop);
435 propBE.parentUniqueId = this.ConnectionWizardServiceNg2.selectedMatch.relationship.relation.capabilityOwnerId;
438 if (capabilityPropertiesBE.length > 0) {
439 // if there are capability properties to update, then first update capability properties and then resolve promise
440 this.ComponentInstanceServiceNg2
441 .updateInstanceCapabilityProperties(
443 this.ConnectionWizardServiceNg2.selectedMatch.toNode,
444 this.ConnectionWizardServiceNg2.selectedMatch.capability,
445 capabilityPropertiesBE
447 .subscribe((response) => {
448 console.log("Update resource instance capability properties response: ", response);
449 this.ConnectionWizardServiceNg2.changedCapabilityProperties = [];
450 resolve(capabilityPropertiesBE);
453 // no capability properties to update, immediately resolve promise
454 resolve(capabilityPropertiesBE);
459 scope.createLinkFromMenu = (): void => {
460 scope.isLinkMenuOpen = false;
462 scope.saveChangedCapabilityProperties().then(() => {
464 this.CompositionGraphLinkUtils
465 .createLinkFromMenu(this._cy, this.ConnectionWizardServiceNg2.selectedMatch, scope.component);
469 scope.hideRelationMenu = () => {
470 this.commonGraphUtils.safeApply(scope, () => {
471 delete scope.canvasMenuProps;
472 this.$timeout.cancel(scope.relationMenuTimeout);
476 scope.createOrUpdateServicePath = (data: any) => {
477 this.servicePathGraphUtils.createOrUpdateServicePath(scope, data);
479 scope.deletePathsOnCy = () => {
480 this.servicePathGraphUtils.deletePathsFromGraph(this._cy, <Service>scope.component);
482 scope.drawPathOnCy = (data: ForwardingPath) => {
483 this.servicePathGraphUtils.drawPath(this._cy, data, <Service>scope.component);
486 scope.viewRelation = (link: Cy.CollectionEdges) => {
487 scope.hideRelationMenu();
489 const linkData = link.data();
490 const sourceNode: CompositionCiNodeBase = link.source().data();
491 const targetNode: CompositionCiNodeBase = link.target().data();
492 const relationship: Relationship = linkData.relation.relationships[0];
494 scope.component.getRelationRequirementCapability(relationship, sourceNode.componentInstance, targetNode.componentInstance).then((objReqCap) => {
495 const capability = objReqCap.capability;
496 const requirement = objReqCap.requirement;
498 this.ConnectionWizardServiceNg2.currentComponent = scope.component;
499 this.ConnectionWizardServiceNg2.connectRelationModel = new ConnectRelationModel(sourceNode, targetNode, []);
500 this.ConnectionWizardServiceNg2.selectedMatch = new Match(requirement, capability, true, linkData.source, linkData.target);
501 this.ConnectionWizardServiceNg2.selectedMatch.relationship = relationship;
503 const title = `Connection Properties`;
504 const saveButton: ButtonModel = new ButtonModel('Save', 'blue', () => {
505 scope.saveChangedCapabilityProperties().then(() => {
506 this.ModalServiceNg2.closeCurrentModal();
509 const cancelButton: ButtonModel = new ButtonModel('Cancel', 'white', () => {
510 this.ModalServiceNg2.closeCurrentModal();
512 const modal = new ModalModel('xl', title, '', [saveButton, cancelButton]);
513 const modalInstance = this.ModalServiceNg2.createCustomModal(modal);
514 this.ModalServiceNg2.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent);
515 modalInstance.instance.open();
517 new Promise((resolve) => {
518 if (!this.ConnectionWizardServiceNg2.selectedMatch.capability.properties) {
519 this.ComponentInstanceServiceNg2.getInstanceCapabilityProperties(scope.component, linkData.target, capability)
528 this.ModalServiceNg2.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent);
535 scope.deleteRelation = (link: Cy.CollectionEdges) => {
536 scope.hideRelationMenu();
538 //if multiple edges selected, delete the VL itself so edges get deleted automatically
539 if (this._cy.$('edge:selected').length > 1) {
540 this.NodesGraphUtils.deleteNode(this._cy, scope.component, this._cy.$('node:selected'));
542 this.CompositionGraphLinkUtils.deleteLink(this._cy, scope.component, true, link);
547 private registerCytoscapeGraphEvents(scope: ICompositionGraphScope) {
549 this._cy.on('addedgemouseup', (event, data) => {
550 scope.relationMenuDirectiveObj = this.CompositionGraphLinkUtils.onLinkDrawn(this._cy, data.source, data.target);
551 if (scope.relationMenuDirectiveObj != null) {
552 this.ConnectionWizardServiceNg2.setRelationMenuDirectiveObj(scope.relationMenuDirectiveObj);
553 this.ConnectionWizardServiceNg2.currentComponent = scope.component;
554 //TODO: init with the selected values
555 this.ConnectionWizardServiceNg2.selectedMatch = null;
557 let steps: Array<StepModel> = [];
558 let fromNodeName: string = scope.relationMenuDirectiveObj.fromNode.componentInstance.name;
559 let toNodeName: string = scope.relationMenuDirectiveObj.toNode.componentInstance.name;
560 steps.push(new StepModel(fromNodeName, FromNodeStepComponent));
561 steps.push(new StepModel(toNodeName, ToNodeStepComponent));
562 steps.push(new StepModel('Properties', PropertiesStepComponent));
563 let wizardTitle = 'Connect: ' + fromNodeName + ' to ' + toNodeName;
564 let modalInstance = this.ModalServiceNg2.createMultiStepsWizard(wizardTitle, steps, scope.createLinkFromMenu, ConnectionWizardHeaderComponent);
565 modalInstance.instance.open();
568 this._cy.on('tapstart', 'node', (event: Cy.EventObject) => {
569 scope.isOnDrag = true;
570 this._currentlyCLickedNodePosition = angular.copy(event.cyTarget[0].position()); //update node position on drag
571 if (event.cyTarget.data().isUcpe) {
572 this._cy.nodes('.ucpe-cp').unlock();
573 event.cyTarget.style('opacity', 0.5);
577 this._cy.on('drag', 'node', (event: Cy.EventObject) => {
579 if (event.cyTarget.data().isDraggable) {
580 event.cyTarget.style({ 'overlay-opacity': 0.24 });
581 if (this.GeneralGraphUtils.isValidDrop(this._cy, event.cyTarget)) {
582 event.cyTarget.style({ 'overlay-color': GraphColors.NODE_BACKGROUND_COLOR });
584 event.cyTarget.style({ 'overlay-color': GraphColors.NODE_OVERLAPPING_BACKGROUND_COLOR });
588 if (event.cyTarget.data().isUcpe) {
589 let pos = event.cyTarget.position();
591 this._cy.nodes('[?isInsideGroup]').positions((i, node) => {
593 x: pos.x + node.data("ucpeOffset").x,
594 y: pos.y + node.data("ucpeOffset").y
600 this._cy.on('handlemouseover', (event, payload) => {
602 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
606 if (scope.zoneTagMode) {
607 scope.zoneTagMode = scope.zones[scope.activeZoneInstance.type].getHoverTagModeId();
611 let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes());
612 let nodesLinks = this.GeneralGraphUtils.getAllCompositionCiLinks(this._cy);
614 let linkableNodes = this.commonGraphUtils.getLinkableNodes(this._cy, payload.node);
615 let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findMatchingNodes(payload.node.data().componentInstance, linkableNodes, nodesLinks);
616 this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
617 this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy, payload.node.data());
619 this._cy.scratch()._edge_editation_highlights = true;
622 this._cy.on('handlemouseout', () => {
623 if (scope.zoneTagMode) {
624 scope.zoneTagMode = scope.zones[scope.activeZoneInstance.type].getTagModeId();
627 if (this._cy.scratch('_edge_editation_highlights') === true) {
628 this._cy.removeScratch('_edge_editation_highlights');
629 this._cy.emit('hidehandles');
630 this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
635 this._cy.on('tapend', (event: Cy.EventObject) => {
636 scope.isOnDrag = false;
637 if (scope.zoneTagMode) {
640 if (event.cyTarget === this._cy) { //On Background clicked
641 if (this._cy.$('node:selected').length === 0) { //if the background click but not dragged
642 if (scope.activeZoneInstance) {
643 scope.unsetActiveZoneInstance();
645 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED);
647 scope.hideRelationMenu();
650 else if (event.cyTarget.isEdge()) { //On Edge clicked
651 this.CompositionGraphLinkUtils.handleLinkClick(this._cy, event);
652 if (event.cyTarget.data().type === CompositionCiServicePathLink.LINK_TYPE) {
655 this.openModifyLinkMenu(scope, this.CompositionGraphLinkUtils.getModifyLinkMenu(event.cyTarget[0], event), 6000);
658 else { //On Node clicked
660 this._cy.nodes(':grabbed').style({ 'overlay-opacity': 0 });
662 let isUcpe: boolean = event.cyTarget.data().isUcpe;
663 let newPosition = event.cyTarget[0].position();
664 //node position changed (drop after drag event) - we need to update position
665 if (this._currentlyCLickedNodePosition.x !== newPosition.x || this._currentlyCLickedNodePosition.y !== newPosition.y) {
666 let nodesMoved: Cy.CollectionNodes = this._cy.$(':grabbed');
668 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.
670 this.NodesGraphUtils.onNodesPositionChanged(this._cy, scope.component, nodesMoved);
672 this.$log.debug('composition-graph::onNodeSelectedEvent:: fired');
673 if (scope.activeZoneInstance) {
674 scope.unsetActiveZoneInstance();
677 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_NODE_SELECTED, event.cyTarget.data().componentInstance);
682 this._cy.nodes('.ucpe-cp').lock();
683 event.cyTarget.style('opacity', 1);
689 this._cy.on('boxselect', 'node', (event: Cy.EventObject) => {
690 scope.unsetActiveZoneInstance();
691 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_NODE_SELECTED, event.cyTarget.data().componentInstance);
694 this._cy.on('canvasredraw', (event: Cy.EventObject) => {
695 if (scope.zoneTagMode) {
696 this.compositionGraphZoneUtils.showZoneTagIndications(this._cy, scope.activeZoneInstance);
700 this._cy.on('handletagclick', (event: Cy.EventObject, eventData: any) => {
701 this.compositionGraphZoneUtils.handleTagClick(this._cy, scope.activeZoneInstance, eventData.nodeId);
707 private openModifyLinkMenu = (scope: ICompositionGraphScope, linkMenuObject: LinkMenu, timeOutInMilliseconds?: number) => {
708 scope.hideRelationMenu();
709 this.$timeout(() => {
710 scope.canvasMenuProps = {
712 styleClass: 'w-sdc-canvas-menu-list',
715 x: `${linkMenuObject.position.x}px`,
716 y: `${linkMenuObject.position.y}px`
720 if (this._cy.$('edge:selected').length === 1) {
721 scope.canvasMenuProps.items.push({
723 styleClass: 'w-sdc-canvas-menu-item-view',
725 scope.viewRelation(<Cy.CollectionEdges>linkMenuObject.link);
729 if (!scope.isViewOnly) {
730 scope.canvasMenuProps.items.push({
732 styleClass: 'w-sdc-canvas-menu-item-delete',
734 scope.deleteRelation(<Cy.CollectionEdges>linkMenuObject.link);
738 scope.relationMenuTimeout = this.$timeout(() => {
739 scope.hideRelationMenu();
740 }, timeOutInMilliseconds ? timeOutInMilliseconds : 6000);
744 private initGraphNodes(componentInstances: ComponentInstance[], isViewOnly: boolean) {
748 let handles = new CytoscapeEdgeEditation;
749 handles.init(this._cy);
750 if (!isViewOnly) { //Init nodes handle extension - enable dynamic links
751 handles.initNodeEvents();
752 handles.registerHandle(ComponentInstanceNodesStyle.getAddEdgeHandle());
754 handles.registerHandle(ComponentInstanceNodesStyle.getTagHandle());
755 handles.registerHandle(ComponentInstanceNodesStyle.getTaggedPolicyHandle());
756 handles.registerHandle(ComponentInstanceNodesStyle.getTaggedGroupHandle());
760 _.each(componentInstances, (instance) => {
761 let compositionGraphNode: CompositionCiNodeBase = this.NodesFactory.createNode(instance);
762 this.commonGraphUtils.addComponentInstanceNodeToGraph(this._cy, compositionGraphNode);
767 private initDropZone(scope: ICompositionGraphScope) {
769 if (scope.isViewOnly) {
772 scope.dropCallback = (event: IDragDropEvent) => {
773 this.$log.debug(`composition-graph::dropCallback:: fired`);
774 this.CompositionGraphPaletteUtils.addNodeFromPalette(this._cy, event, scope.component);
777 scope.verifyDrop = (event: JQueryEventObject) => {
779 if (!this.dragElement || this.dragElement.hasClass('red')) {
785 scope.beforeDropCallback = (event: IDragDropEvent): ng.IPromise<void> => {
786 let deferred: ng.IDeferred<void> = this.$q.defer<void>();
787 if (this.dragElement.hasClass('red')) {
793 return deferred.promise;
798 private initZones = (scope: ICompositionGraphScope): void => {
799 scope.zones = this.compositionGraphZoneUtils.createCompositionZones();
802 scope.zoneMinimizeToggle = (zoneType: ZoneInstanceType): void => {
803 scope.zones[zoneType].minimized = !scope.zones[zoneType].minimized;
806 scope.zoneInstanceModeChanged = (newMode: ZoneInstanceMode, instance: ZoneInstance, zoneId: ZoneInstanceType): void => {
807 if (scope.zoneTagMode) { //we're in tag mode.
808 if (instance == scope.activeZoneInstance && newMode == ZoneInstanceMode.NONE) { //we want to turn tag mode off.
809 scope.zoneTagMode = null;
810 scope.activeZoneInstance.mode = ZoneInstanceMode.SELECTED;
811 this.compositionGraphZoneUtils.endCyTagMode(this._cy);
812 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_CANVAS_TAG_END, instance);
816 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
817 instance.mode = newMode;
820 if (newMode == ZoneInstanceMode.NONE) {
821 this.compositionGraphZoneUtils.hideZoneTagIndications(this._cy);
822 if (scope.zones[ZoneInstanceType.GROUP]) {
823 this.compositionGraphZoneUtils.hideGroupZoneIndications(scope.zones[ZoneInstanceType.GROUP].instances);
826 if (newMode >= ZoneInstanceMode.HOVER) {
827 this.compositionGraphZoneUtils.showZoneTagIndications(this._cy, instance);
828 if (instance.type == ZoneInstanceType.POLICY && scope.zones[ZoneInstanceType.GROUP]) {
829 this.compositionGraphZoneUtils.showGroupZoneIndications(scope.zones[ZoneInstanceType.GROUP].instances, instance);
832 if (newMode >= ZoneInstanceMode.SELECTED) {
833 this._cy.$('node:selected').unselect();
834 if (scope.activeZoneInstance && scope.activeZoneInstance != instance && newMode >= ZoneInstanceMode.SELECTED) {
835 scope.activeZoneInstance.mode = ZoneInstanceMode.NONE;
837 scope.activeZoneInstance = instance;
838 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ZONE_INSTANCE_SELECTED, instance);
840 if (newMode == ZoneInstanceMode.TAG) {
841 this.compositionGraphZoneUtils.startCyTagMode(this._cy);
842 scope.zoneTagMode = scope.zones[zoneId].getTagModeId();
843 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_CANVAS_TAG_START, zoneId);
848 scope.zoneInstanceTagged = (taggedInstance: ZoneInstance) => {
849 scope.activeZoneInstance.addOrRemoveAssignment(taggedInstance.instanceData.uniqueId, ZoneInstanceAssignmentType.GROUPS);
850 let newHandle: string = this.compositionGraphZoneUtils.getCorrectHandleForNode(taggedInstance.instanceData.uniqueId, scope.activeZoneInstance);
851 taggedInstance.showHandle(newHandle);
854 scope.zoneBackgroundClicked = (): void => {
855 if (!scope.zoneTagMode && scope.activeZoneInstance) {
856 scope.unsetActiveZoneInstance();
860 scope.zoneAssignmentSaveStart = () => {
861 this.LoaderService.showLoader('composition-graph');
864 scope.zoneAssignmentSaveComplete = (success: boolean) => {
865 this.LoaderService.hideLoader('composition-graph');
867 this.GeneralGraphUtils.showUpdateFailure();
871 scope.unsetActiveZoneInstance = (): void => {
872 if (scope.activeZoneInstance) {
873 scope.activeZoneInstance.mode = ZoneInstanceMode.NONE;
874 scope.activeZoneInstance = null;
875 scope.zoneTagMode = null;
876 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED);
882 public static factory = ($q,
888 ComponentInstanceFactory,
890 EventListenerService,
894 MatchCapabilitiesRequirementsUtils,
895 CompositionGraphPaletteUtils,
896 CompositionGraphZoneUtils,
899 ConnectionWizardService,
900 ComponentInstanceServiceNg2,
901 ServicePathGraphUtils) => {
902 return new CompositionGraph(
909 ComponentInstanceFactory,
911 EventListenerService,
915 MatchCapabilitiesRequirementsUtils,
916 CompositionGraphPaletteUtils,
917 CompositionGraphZoneUtils,
920 ConnectionWizardService,
921 ComponentInstanceServiceNg2,
922 ServicePathGraphUtils);
926 CompositionGraph.factory.$inject = [
931 'CompositionGraphLinkUtils',
932 'CompositionGraphGeneralUtils',
933 'ComponentInstanceFactory',
934 'CompositionGraphNodesUtils',
935 'EventListenerService',
939 'MatchCapabilitiesRequirementsUtils',
940 'CompositionGraphPaletteUtils',
941 'CompositionGraphZoneUtils',
942 'ComponentServiceNg2',
944 'ConnectionWizardServiceNg2',
945 'ComponentInstanceServiceNg2',
946 'ServicePathGraphUtils'