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;
124 canvasMenuProps: any;
126 createOrUpdateServicePath(data: any): void;
127 deletePathsOnCy(): void;
128 drawPathOnCy(data: ForwardingPath): void;
129 selectedPathId: string;
132 export class CompositionGraph implements ng.IDirective {
133 private _cy: Cy.Instance;
134 private _currentlyCLickedNodePosition: Cy.Position;
135 private dragElement: JQuery;
136 private dragComponent: ComponentInstance;
138 constructor(private $q: ng.IQService,
139 private $log: ng.ILogService,
140 private $timeout: ng.ITimeoutService,
141 private NodesFactory: NodesFactory,
142 private CompositionGraphLinkUtils: CompositionGraphLinkUtils,
143 private GeneralGraphUtils: CompositionGraphGeneralUtils,
144 private ComponentInstanceFactory: ComponentInstanceFactory,
145 private NodesGraphUtils: CompositionGraphNodesUtils,
146 private eventListenerService: EventListenerService,
147 private ComponentFactory: ComponentFactory,
148 private LoaderService: LoaderService,
149 private commonGraphUtils: CommonGraphUtils,
150 private matchCapabilitiesRequirementsUtils: MatchCapabilitiesRequirementsUtils,
151 private CompositionGraphPaletteUtils: CompositionGraphPaletteUtils,
152 private compositionGraphZoneUtils: CompositionGraphZoneUtils,
153 private ComponentServiceNg2: ComponentServiceNg2,
154 private ModalServiceNg2: ModalService,
155 private ConnectionWizardServiceNg2: ConnectionWizardService,
156 private ComponentInstanceServiceNg2: ComponentInstanceServiceNg2,
157 private servicePathGraphUtils: ServicePathGraphUtils) {
162 template = require('./composition-graph.html');
169 link = (scope: ICompositionGraphScope, el: JQuery) => {
170 this.loadGraph(scope, el);
172 if (!scope.component.groupInstances || !scope.component.policies) {
173 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED, () => {
174 this.loadGraphData(scope);
177 this.loadGraphData(scope);
181 scope.$on('$destroy', () => {
183 _.forEach(GRAPH_EVENTS, (event) => {
184 this.eventListenerService.unRegisterObserver(event);
186 this.eventListenerService.unRegisterObserver(EVENTS.SHOW_LOADER_EVENT + 'composition-graph');
187 this.eventListenerService.unRegisterObserver(EVENTS.HIDE_LOADER_EVENT + 'composition-graph');
192 private loadGraphData = (scope: ICompositionGraphScope) => {
193 this.initGraphNodes(scope.component.componentInstances, scope.isViewOnly);
194 this.commonGraphUtils.initGraphLinks(this._cy, scope.component.componentInstancesRelations, scope.component.getRelationRequirementCapability.bind(scope.component));
195 this.commonGraphUtils.initUcpeChildren(this._cy);
196 this.compositionGraphZoneUtils.initZoneInstances(scope.zones, scope.component);
197 setTimeout(() => {//Need settimeout so that angular canvas changes will take effect before resize & center
198 this.GeneralGraphUtils.zoomAllWithMax(this._cy, 1);
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);
212 private initGraph(graphEl: JQuery, isViewOnly: boolean) {
214 this._cy = cytoscape({
216 style: ComponentInstanceNodesStyle.getCompositionGraphStyle(),
217 zoomingEnabled: true,
220 userZoomingEnabled: false,
221 userPanningEnabled: true,
222 selectionType: 'single',
223 boxSelectionEnabled: true,
224 autolock: isViewOnly,
225 autoungrabify: isViewOnly
229 private initViewMode(isViewOnly: boolean) {
232 //remove event listeners
233 this._cy.off('drag');
234 this._cy.off('handlemouseout');
235 this._cy.off('handlemouseover');
236 this._cy.off('canvasredraw');
237 this._cy.off('handletagclick')
238 this._cy.edges().unselectify();
242 private registerCustomEvents(scope: ICompositionGraphScope, el: JQuery) {
244 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_GROUP_INSTANCE_UPDATE, (groupInstance: GroupInstance) => {
245 this.compositionGraphZoneUtils.findAndUpdateZoneInstanceData(scope.zones, groupInstance);
246 this.GeneralGraphUtils.showGroupUpdateSuccess();
249 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_POLICY_INSTANCE_UPDATE, (policyInstance: PolicyInstance) => {
250 this.compositionGraphZoneUtils.findAndUpdateZoneInstanceData(scope.zones, policyInstance);
251 this.GeneralGraphUtils.showPolicyUpdateSuccess();
254 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_IN, (leftPaletteComponent: LeftPaletteComponent) => {
255 if (scope.isOnDrag) {
259 this.$log.info(`composition-graph::registerEventServiceEvents:: palette hover on component: ${leftPaletteComponent.uniqueId}`);
261 let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes());
262 let nodesLinks = this.GeneralGraphUtils.getAllCompositionCiLinks(this._cy);
264 if (this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.containsKey(leftPaletteComponent.uniqueId)) {
265 let cacheComponent = this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.getValue(leftPaletteComponent.uniqueId);
266 let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findMatchingNodes(cacheComponent, nodesData, nodesLinks);
268 this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
269 this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy);
274 //----------------------- ORIT TO FIX------------------------//
276 this.ComponentServiceNg2.getCapabilitiesAndRequirements(leftPaletteComponent.componentType, leftPaletteComponent.uniqueId).subscribe((response: ComponentGenericResponse) => {
278 let component = this.ComponentFactory.createEmptyComponent(leftPaletteComponent.componentType);
279 component.uniqueId = component.uniqueId;
280 component.capabilities = response.capabilities;
281 component.requirements = response.requirements;
282 this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.setValue(leftPaletteComponent.uniqueId, component);
286 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ADD_ZONE_INSTANCE_FROM_PALETTE, (component: Component, paletteComponent: LeftPaletteComponent, startPosition: Point) => {
288 let zoneType: ZoneInstanceType = this.compositionGraphZoneUtils.getZoneTypeForPaletteComponent(paletteComponent.categoryType);
289 this.compositionGraphZoneUtils.showZone(scope.zones[zoneType]);
291 this.LoaderService.showLoader('composition-graph');
292 this.compositionGraphZoneUtils.createZoneInstanceFromLeftPalette(zoneType, component, paletteComponent.type).subscribe((zoneInstance: ZoneInstance) => {
293 this.LoaderService.hideLoader('composition-graph');
294 this.compositionGraphZoneUtils.addInstanceToZone(scope.zones[zoneInstance.type], zoneInstance, true);
295 this.compositionGraphZoneUtils.createPaletteToZoneAnimation(startPosition, zoneType, zoneInstance);
297 this.LoaderService.hideLoader('composition-graph');
301 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_OUT, () => {
303 this._cy.emit('hidehandles');
304 this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
307 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_START, (dragElement, dragComponent) => {
309 this.dragElement = dragElement;
310 this.dragComponent = this.ComponentInstanceFactory.createComponentInstanceFromComponent(dragComponent);
313 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_ACTION, (event: IDragDropEvent) => {
314 this.CompositionGraphPaletteUtils.onComponentDrag(this._cy, event, this.dragElement, this.dragComponent);
318 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, (component: ComponentInstance) => {
320 let selectedNode = this._cy.getElementById(component.uniqueId);
321 selectedNode.data().componentInstance.name = component.name;
322 selectedNode.data('name', component.name); //used for tooltip
323 selectedNode.data('displayName', selectedNode.data().getDisplayName()); //abbreviated
327 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, (componentInstance: ComponentInstance) => {
328 let nodeToDelete = this._cy.getElementById(componentInstance.uniqueId);
329 this.NodesGraphUtils.deleteNode(this._cy, scope.component, nodeToDelete);
332 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_ZONE_INSTANCE, (deletedInstance: UIZoneInstanceObject) => {
334 if (deletedInstance.type === ZoneInstanceType.POLICY) {
335 scope.component.policies = scope.component.policies.filter(policy => policy.uniqueId !== deletedInstance.uniqueId);
336 } else if (deletedInstance.type === ZoneInstanceType.GROUP) {
337 scope.component.groupInstances = scope.component.groupInstances.filter(group => group.uniqueId !== deletedInstance.uniqueId);
339 //remove it from zones
340 scope.zones[deletedInstance.type].removeInstance(deletedInstance.uniqueId);
341 if (deletedInstance.type === ZoneInstanceType.GROUP && !_.isEmpty(scope.zones[ZoneInstanceType.POLICY])) {
342 this.compositionGraphZoneUtils.updateTargetsOrMembersOnCanvasDelete(deletedInstance.uniqueId, [scope.zones[ZoneInstanceType.POLICY]], ZoneInstanceAssignmentType.GROUPS);
344 this.eventListenerService.notifyObservers(EVENTS.UPDATE_PANEL);
347 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE_SUCCESS, (componentInstanceId: string) => {
348 if (!_.isEmpty(scope.zones)) {
349 this.compositionGraphZoneUtils.updateTargetsOrMembersOnCanvasDelete(componentInstanceId, scope.zones, ZoneInstanceAssignmentType.COMPONENT_INSTANCES);
353 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_EDGE, (releaseLoading: boolean, linksToDelete: Cy.CollectionEdges) => {
354 this.CompositionGraphLinkUtils.deleteLink(this._cy, scope.component, releaseLoading, linksToDelete);
357 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_INSERT_NODE_TO_UCPE, (node: Cy.CollectionNodes, ucpe: Cy.CollectionNodes, updateExistingNode: boolean) => {
359 this.commonGraphUtils.initUcpeChildData(node, ucpe);
360 //check if item is a VL, and if so, skip adding the binding to ucpe
361 if (!(node.data() instanceof CompositionCiNodeVl)) {
362 this.CompositionGraphLinkUtils.createVfToUcpeLink(scope.component, this._cy, ucpe.data(), node.data()); //create link from the node to the ucpe
365 if (updateExistingNode) {
366 let vlsPendingDeletion: Cy.CollectionNodes = this.NodesGraphUtils.deleteNodeVLsUponMoveToOrFromUCPE(scope.component, node.cy(), node); //delete connected VLs that no longer have 2 links
367 this.CompositionGraphLinkUtils.deleteLinksWhenNodeMovedFromOrToUCPE(scope.component, node.cy(), node, vlsPendingDeletion); //delete all connected links if needed
368 this.GeneralGraphUtils.pushUpdateComponentInstanceActionToQueue(scope.component, true, node.data().componentInstance); //update componentInstance position
373 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_REMOVE_NODE_FROM_UCPE, (node: Cy.CollectionNodes, ucpe: Cy.CollectionNodes) => {
374 this.commonGraphUtils.removeUcpeChildData(node);
375 let vlsPendingDeletion: Cy.CollectionNodes = this.NodesGraphUtils.deleteNodeVLsUponMoveToOrFromUCPE(scope.component, node.cy(), node);
376 this.CompositionGraphLinkUtils.deleteLinksWhenNodeMovedFromOrToUCPE(scope.component, node.cy(), node, vlsPendingDeletion); //delete all connected links if needed
377 this.GeneralGraphUtils.pushUpdateComponentInstanceActionToQueue(scope.component, true, node.data().componentInstance); //update componentInstance position
380 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_VERSION_CHANGED, (component: Component) => {
381 scope.component = component;
382 this._cy.elements().remove();
383 this.loadGraphData(scope);
387 scope.zoom = (zoomIn: boolean): void => {
388 let currentZoom: number = this._cy.zoom();
390 this.GeneralGraphUtils.zoomGraphTo(this._cy, currentZoom + .1);
392 this.GeneralGraphUtils.zoomGraphTo(this._cy, currentZoom - .1);
397 scope.zoomAllWithoutSidebar = () => {
398 scope.withSidebar = false;
399 setTimeout(() => { //wait for sidebar changes to take effect before zooming
400 this.GeneralGraphUtils.zoomAll(this._cy);
404 scope.getAutoCompleteValues = (searchTerm: string) => {
405 if (searchTerm.length > 1) { //US requirement: only display search results after 2nd letter typed.
406 let nodes: Cy.CollectionNodes = this.NodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
407 scope.componentInstanceNames = _.map(nodes, node => node.data('name'));
409 scope.componentInstanceNames = [];
413 scope.highlightSearchMatches = (searchTerm: string) => {
414 this.NodesGraphUtils.highlightMatchingNodesByName(this._cy, searchTerm);
415 let matchingNodes: Cy.CollectionNodes = this.NodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
416 this.GeneralGraphUtils.zoomAll(this._cy, matchingNodes);
419 scope.saveChangedCapabilityProperties = (): Promise<PropertyBEModel[]> => {
420 return new Promise<PropertyBEModel[]>((resolve) => {
421 const capabilityPropertiesBE: PropertyBEModel[] = this.ConnectionWizardServiceNg2.changedCapabilityProperties.map((prop) => {
422 prop.value = prop.getJSONValue();
423 const propBE = new PropertyBEModel(prop);
424 propBE.parentUniqueId = this.ConnectionWizardServiceNg2.selectedMatch.relationship.relation.capabilityOwnerId;
427 if (capabilityPropertiesBE.length > 0) {
428 // if there are capability properties to update, then first update capability properties and then resolve promise
429 this.ComponentInstanceServiceNg2
430 .updateInstanceCapabilityProperties(
432 this.ConnectionWizardServiceNg2.selectedMatch.toNode,
433 this.ConnectionWizardServiceNg2.selectedMatch.capability,
434 capabilityPropertiesBE
436 .subscribe((response) => {
437 console.log("Update resource instance capability properties response: ", response);
438 this.ConnectionWizardServiceNg2.changedCapabilityProperties = [];
439 resolve(capabilityPropertiesBE);
442 // no capability properties to update, immediately resolve promise
443 resolve(capabilityPropertiesBE);
448 scope.createLinkFromMenu = (): void => {
449 scope.isLinkMenuOpen = false;
451 scope.saveChangedCapabilityProperties().then(() => {
453 this.CompositionGraphLinkUtils
454 .createLinkFromMenu(this._cy, this.ConnectionWizardServiceNg2.selectedMatch, scope.component);
458 scope.hideRelationMenu = () => {
459 this.commonGraphUtils.safeApply(scope, () => {
460 delete scope.canvasMenuProps;
461 this.$timeout.cancel(scope.relationMenuTimeout);
465 scope.createOrUpdateServicePath = (data: any) => {
466 this.servicePathGraphUtils.createOrUpdateServicePath(scope, data);
468 scope.deletePathsOnCy = () => {
469 this.servicePathGraphUtils.deletePathsFromGraph(this._cy, <Service>scope.component);
471 scope.drawPathOnCy = (data: ForwardingPath) => {
472 this.servicePathGraphUtils.drawPath(this._cy, data, <Service>scope.component);
475 scope.viewRelation = (link: Cy.CollectionEdges) => {
476 scope.hideRelationMenu();
478 const linkData = link.data();
479 const sourceNode: CompositionCiNodeBase = link.source().data();
480 const targetNode: CompositionCiNodeBase = link.target().data();
481 const relationship: Relationship = linkData.relation.relationships[0];
483 scope.component.getRelationRequirementCapability(relationship, sourceNode.componentInstance, targetNode.componentInstance).then((objReqCap) => {
484 const capability = objReqCap.capability;
485 const requirement = objReqCap.requirement;
487 this.ConnectionWizardServiceNg2.currentComponent = scope.component;
488 this.ConnectionWizardServiceNg2.connectRelationModel = new ConnectRelationModel(sourceNode, targetNode, []);
489 this.ConnectionWizardServiceNg2.selectedMatch = new Match(requirement, capability, true, linkData.source, linkData.target);
490 this.ConnectionWizardServiceNg2.selectedMatch.relationship = relationship;
492 const title = `Connection Properties`;
493 const saveButton: ButtonModel = new ButtonModel('Save', 'blue', () => {
494 scope.saveChangedCapabilityProperties().then(() => {
495 this.ModalServiceNg2.closeCurrentModal();
498 const cancelButton: ButtonModel = new ButtonModel('Cancel', 'white', () => {
499 this.ModalServiceNg2.closeCurrentModal();
501 const modal = new ModalModel('xl', title, '', [saveButton, cancelButton]);
502 const modalInstance = this.ModalServiceNg2.createCustomModal(modal);
503 this.ModalServiceNg2.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent);
504 modalInstance.instance.open();
506 new Promise((resolve) => {
507 if (!this.ConnectionWizardServiceNg2.selectedMatch.capability.properties) {
508 this.ComponentInstanceServiceNg2.getInstanceCapabilityProperties(scope.component, linkData.target, capability)
517 this.ModalServiceNg2.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent);
524 scope.deleteRelation = (link: Cy.CollectionEdges) => {
525 scope.hideRelationMenu();
527 //if multiple edges selected, delete the VL itself so edges get deleted automatically
528 if (this._cy.$('edge:selected').length > 1) {
529 this.NodesGraphUtils.deleteNode(this._cy, scope.component, this._cy.$('node:selected'));
531 this.CompositionGraphLinkUtils.deleteLink(this._cy, scope.component, true, link);
536 private registerCytoscapeGraphEvents(scope: ICompositionGraphScope) {
538 this._cy.on('addedgemouseup', (event, data) => {
539 scope.relationMenuDirectiveObj = this.CompositionGraphLinkUtils.onLinkDrawn(this._cy, data.source, data.target);
540 if (scope.relationMenuDirectiveObj != null) {
541 this.ConnectionWizardServiceNg2.setRelationMenuDirectiveObj(scope.relationMenuDirectiveObj);
542 this.ConnectionWizardServiceNg2.currentComponent = scope.component;
543 //TODO: init with the selected values
544 this.ConnectionWizardServiceNg2.selectedMatch = null;
546 let steps: Array<StepModel> = [];
547 let fromNodeName: string = scope.relationMenuDirectiveObj.fromNode.componentInstance.name;
548 let toNodeName: string = scope.relationMenuDirectiveObj.toNode.componentInstance.name;
549 steps.push(new StepModel(fromNodeName, FromNodeStepComponent));
550 steps.push(new StepModel(toNodeName, ToNodeStepComponent));
551 steps.push(new StepModel('Properties', PropertiesStepComponent));
552 let wizardTitle = 'Connect: ' + fromNodeName + ' to ' + toNodeName;
553 let modalInstance = this.ModalServiceNg2.createMultiStepsWizard(wizardTitle, steps, scope.createLinkFromMenu, ConnectionWizardHeaderComponent);
554 modalInstance.instance.open();
557 this._cy.on('tapstart', 'node', (event: Cy.EventObject) => {
558 scope.isOnDrag = true;
559 this._currentlyCLickedNodePosition = angular.copy(event.cyTarget[0].position()); //update node position on drag
560 if (event.cyTarget.data().isUcpe) {
561 this._cy.nodes('.ucpe-cp').unlock();
562 event.cyTarget.style('opacity', 0.5);
566 this._cy.on('drag', 'node', (event: Cy.EventObject) => {
568 if (event.cyTarget.data().isDraggable) {
569 event.cyTarget.style({ 'overlay-opacity': 0.24 });
570 if (this.GeneralGraphUtils.isValidDrop(this._cy, event.cyTarget)) {
571 event.cyTarget.style({ 'overlay-color': GraphColors.NODE_BACKGROUND_COLOR });
573 event.cyTarget.style({ 'overlay-color': GraphColors.NODE_OVERLAPPING_BACKGROUND_COLOR });
577 if (event.cyTarget.data().isUcpe) {
578 let pos = event.cyTarget.position();
580 this._cy.nodes('[?isInsideGroup]').positions((i, node) => {
582 x: pos.x + node.data("ucpeOffset").x,
583 y: pos.y + node.data("ucpeOffset").y
589 this._cy.on('handlemouseover', (event, payload) => {
591 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
595 if (scope.zoneTagMode) {
596 scope.zoneTagMode = scope.zones[scope.activeZoneInstance.type].getHoverTagModeId();
600 let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes());
601 let nodesLinks = this.GeneralGraphUtils.getAllCompositionCiLinks(this._cy);
603 let linkableNodes = this.commonGraphUtils.getLinkableNodes(this._cy, payload.node);
604 let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findMatchingNodes(payload.node.data().componentInstance, linkableNodes, nodesLinks);
605 this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
606 this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy, payload.node.data());
608 this._cy.scratch()._edge_editation_highlights = true;
611 this._cy.on('handlemouseout', () => {
612 if (scope.zoneTagMode) {
613 scope.zoneTagMode = scope.zones[scope.activeZoneInstance.type].getTagModeId();
616 if (this._cy.scratch('_edge_editation_highlights') === true) {
617 this._cy.removeScratch('_edge_editation_highlights');
618 this._cy.emit('hidehandles');
619 this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
624 this._cy.on('tapend', (event: Cy.EventObject) => {
625 scope.isOnDrag = false;
626 if (scope.zoneTagMode) {
629 if (event.cyTarget === this._cy) { //On Background clicked
630 if (this._cy.$('node:selected').length === 0) { //if the background click but not dragged
631 if (scope.activeZoneInstance) {
632 scope.unsetActiveZoneInstance();
634 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED);
636 scope.hideRelationMenu();
639 else if (event.cyTarget.isEdge()) { //On Edge clicked
640 this.CompositionGraphLinkUtils.handleLinkClick(this._cy, event);
641 if (event.cyTarget.data().type === CompositionCiServicePathLink.LINK_TYPE) {
644 this.openModifyLinkMenu(scope, this.CompositionGraphLinkUtils.getModifyLinkMenu(event.cyTarget[0], event), 6000);
647 else { //On Node clicked
649 this._cy.nodes(':grabbed').style({ 'overlay-opacity': 0 });
651 let isUcpe: boolean = event.cyTarget.data().isUcpe;
652 let newPosition = event.cyTarget[0].position();
653 //node position changed (drop after drag event) - we need to update position
654 if (this._currentlyCLickedNodePosition.x !== newPosition.x || this._currentlyCLickedNodePosition.y !== newPosition.y) {
655 let nodesMoved: Cy.CollectionNodes = this._cy.$(':grabbed');
657 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.
659 this.NodesGraphUtils.onNodesPositionChanged(this._cy, scope.component, nodesMoved);
661 this.$log.debug('composition-graph::onNodeSelectedEvent:: fired');
662 if (scope.activeZoneInstance) {
663 scope.unsetActiveZoneInstance();
666 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_NODE_SELECTED, event.cyTarget.data().componentInstance);
671 this._cy.nodes('.ucpe-cp').lock();
672 event.cyTarget.style('opacity', 1);
678 this._cy.on('boxselect', 'node', (event: Cy.EventObject) => {
679 scope.unsetActiveZoneInstance();
680 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_NODE_SELECTED, event.cyTarget.data().componentInstance);
683 this._cy.on('canvasredraw', (event: Cy.EventObject) => {
684 if (scope.zoneTagMode) {
685 this.compositionGraphZoneUtils.showZoneTagIndications(this._cy, scope.activeZoneInstance);
689 this._cy.on('handletagclick', (event: Cy.EventObject, eventData: any) => {
690 this.compositionGraphZoneUtils.handleTagClick(this._cy, scope.activeZoneInstance, eventData.nodeId);
696 private openModifyLinkMenu = (scope: ICompositionGraphScope, linkMenuObject: LinkMenu, timeOutInMilliseconds?: number) => {
697 scope.hideRelationMenu();
698 this.$timeout(() => {
699 scope.canvasMenuProps = {
701 styleClass: 'w-sdc-canvas-menu-list',
704 x: `${linkMenuObject.position.x}px`,
705 y: `${linkMenuObject.position.y}px`
709 if (this._cy.$('edge:selected').length === 1) {
710 scope.canvasMenuProps.items.push({
712 styleClass: 'w-sdc-canvas-menu-item-view',
714 scope.viewRelation(<Cy.CollectionEdges>linkMenuObject.link);
718 if (!scope.isViewOnly) {
719 scope.canvasMenuProps.items.push({
721 styleClass: 'w-sdc-canvas-menu-item-delete',
723 scope.deleteRelation(<Cy.CollectionEdges>linkMenuObject.link);
727 scope.relationMenuTimeout = this.$timeout(() => {
728 scope.hideRelationMenu();
729 }, timeOutInMilliseconds ? timeOutInMilliseconds : 6000);
733 private initGraphNodes(componentInstances: ComponentInstance[], isViewOnly: boolean) {
737 let handles = new CytoscapeEdgeEditation;
738 handles.init(this._cy);
739 if (!isViewOnly) { //Init nodes handle extension - enable dynamic links
740 handles.initNodeEvents();
741 handles.registerHandle(ComponentInstanceNodesStyle.getAddEdgeHandle());
743 handles.registerHandle(ComponentInstanceNodesStyle.getTagHandle());
744 handles.registerHandle(ComponentInstanceNodesStyle.getTaggedPolicyHandle());
745 handles.registerHandle(ComponentInstanceNodesStyle.getTaggedGroupHandle());
749 _.each(componentInstances, (instance) => {
750 let compositionGraphNode: CompositionCiNodeBase = this.NodesFactory.createNode(instance);
751 this.commonGraphUtils.addComponentInstanceNodeToGraph(this._cy, compositionGraphNode);
756 private initDropZone(scope: ICompositionGraphScope) {
758 if (scope.isViewOnly) {
761 scope.dropCallback = (event: IDragDropEvent) => {
762 this.$log.debug(`composition-graph::dropCallback:: fired`);
763 this.CompositionGraphPaletteUtils.addNodeFromPalette(this._cy, event, scope.component);
766 scope.verifyDrop = (event: JQueryEventObject) => {
768 if (!this.dragElement || this.dragElement.hasClass('red')) {
774 scope.beforeDropCallback = (event: IDragDropEvent): ng.IPromise<void> => {
775 let deferred: ng.IDeferred<void> = this.$q.defer<void>();
776 if (this.dragElement.hasClass('red')) {
782 return deferred.promise;
787 private initZones = (scope: ICompositionGraphScope): void => {
788 scope.zones = this.compositionGraphZoneUtils.createCompositionZones();
791 scope.zoneMinimizeToggle = (zoneType: ZoneInstanceType): void => {
792 scope.zones[zoneType].minimized = !scope.zones[zoneType].minimized;
795 scope.zoneInstanceModeChanged = (newMode: ZoneInstanceMode, instance: ZoneInstance, zoneId: ZoneInstanceType): void => {
796 if (scope.zoneTagMode) { //we're in tag mode.
797 if (instance == scope.activeZoneInstance && newMode == ZoneInstanceMode.NONE) { //we want to turn tag mode off.
798 scope.zoneTagMode = null;
799 scope.activeZoneInstance.mode = ZoneInstanceMode.SELECTED;
800 this.compositionGraphZoneUtils.endCyTagMode(this._cy);
801 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_CANVAS_TAG_END, instance);
805 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
806 instance.mode = newMode;
809 if (newMode == ZoneInstanceMode.NONE) {
810 this.compositionGraphZoneUtils.hideZoneTagIndications(this._cy);
811 if (scope.zones[ZoneInstanceType.GROUP]) {
812 this.compositionGraphZoneUtils.hideGroupZoneIndications(scope.zones[ZoneInstanceType.GROUP].instances);
815 if (newMode >= ZoneInstanceMode.HOVER) {
816 this.compositionGraphZoneUtils.showZoneTagIndications(this._cy, instance);
817 if (instance.type == ZoneInstanceType.POLICY && scope.zones[ZoneInstanceType.GROUP]) {
818 this.compositionGraphZoneUtils.showGroupZoneIndications(scope.zones[ZoneInstanceType.GROUP].instances, instance);
821 if (newMode >= ZoneInstanceMode.SELECTED) {
822 this._cy.$('node:selected').unselect();
823 if (scope.activeZoneInstance && scope.activeZoneInstance != instance && newMode >= ZoneInstanceMode.SELECTED) {
824 scope.activeZoneInstance.mode = ZoneInstanceMode.NONE;
826 scope.activeZoneInstance = instance;
827 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ZONE_INSTANCE_SELECTED, instance);
829 if (newMode == ZoneInstanceMode.TAG) {
830 this.compositionGraphZoneUtils.startCyTagMode(this._cy);
831 scope.zoneTagMode = scope.zones[zoneId].getTagModeId();
832 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_CANVAS_TAG_START, zoneId);
837 scope.zoneInstanceTagged = (taggedInstance: ZoneInstance) => {
838 scope.activeZoneInstance.addOrRemoveAssignment(taggedInstance.instanceData.uniqueId, ZoneInstanceAssignmentType.GROUPS);
839 let newHandle: string = this.compositionGraphZoneUtils.getCorrectHandleForNode(taggedInstance.instanceData.uniqueId, scope.activeZoneInstance);
840 taggedInstance.showHandle(newHandle);
843 scope.zoneBackgroundClicked = (): void => {
844 if (!scope.zoneTagMode && scope.activeZoneInstance) {
845 scope.unsetActiveZoneInstance();
849 scope.zoneAssignmentSaveStart = () => {
850 this.LoaderService.showLoader('composition-graph');
853 scope.zoneAssignmentSaveComplete = (success: boolean) => {
854 this.LoaderService.hideLoader('composition-graph');
856 this.GeneralGraphUtils.showUpdateFailure();
860 scope.unsetActiveZoneInstance = (): void => {
861 if (scope.activeZoneInstance) {
862 scope.activeZoneInstance.mode = ZoneInstanceMode.NONE;
863 scope.activeZoneInstance = null;
864 scope.zoneTagMode = null;
865 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED);
871 public static factory = ($q,
877 ComponentInstanceFactory,
879 EventListenerService,
883 MatchCapabilitiesRequirementsUtils,
884 CompositionGraphPaletteUtils,
885 CompositionGraphZoneUtils,
888 ConnectionWizardService,
889 ComponentInstanceServiceNg2,
890 ServicePathGraphUtils) => {
891 return new CompositionGraph(
898 ComponentInstanceFactory,
900 EventListenerService,
904 MatchCapabilitiesRequirementsUtils,
905 CompositionGraphPaletteUtils,
906 CompositionGraphZoneUtils,
909 ConnectionWizardService,
910 ComponentInstanceServiceNg2,
911 ServicePathGraphUtils);
915 CompositionGraph.factory.$inject = [
920 'CompositionGraphLinkUtils',
921 'CompositionGraphGeneralUtils',
922 'ComponentInstanceFactory',
923 'CompositionGraphNodesUtils',
924 'EventListenerService',
928 'MatchCapabilitiesRequirementsUtils',
929 'CompositionGraphPaletteUtils',
930 'CompositionGraphZoneUtils',
931 'ComponentServiceNg2',
933 'ConnectionWizardServiceNg2',
934 'ComponentInstanceServiceNg2',
935 'ServicePathGraphUtils'