495a243d752ff237f23f59dd7bb51c52fd4a700c
[sdc.git] /
1 /// <reference path="../../../../references"/>
2 module Sdc.Graph.Utils {
3
4     import Dictionary = Sdc.Utils.Dictionary;
5
6     export class CompositionGraphGeneralUtils {
7
8         public componentRequirementsAndCapabilitiesCaching = new Dictionary<string, Models.Components.Component>();
9         protected static graphUtilsUpdateQueue: Sdc.Utils.Functions.QueueUtils;
10
11         constructor(private $q: ng.IQService,
12                     private LoaderService: Services.LoaderService,
13                     private commonGraphUtils: Sdc.Graph.Utils.CommonGraphUtils,
14                     private matchCapabilitiesRequirementsUtils: Graph.Utils.MatchCapabilitiesRequirementsUtils) {
15             CompositionGraphGeneralUtils.graphUtilsUpdateQueue = new Sdc.Utils.Functions.QueueUtils(this.$q);
16         }
17
18
19         /**
20          * Get the offset for the link creation Menu
21          * @param point
22          * @returns {Cy.Position}
23          */
24         public calcMenuOffset: Function = (point: Cy.Position): Cy.Position => {
25             point.x = point.x + 60;
26             point.y = point.y + 105;
27             return point;
28         };
29
30         /**
31          *  return the top left position of the link menu
32          * @param cy
33          * @param targetNodePosition
34          * @returns {Cy.Position}
35          */
36         public getLinkMenuPosition = (cy: Cy.Instance, targetNodePosition: Cy.Position) => {
37             let menuPosition: Cy.Position = this.calcMenuOffset(targetNodePosition); //get the link mid point
38             if (document.body.scrollHeight < menuPosition.y + Sdc.Utils.Constants.GraphUIObjects.LINK_MENU_HEIGHT + $(document.getElementsByClassName('sdc-composition-graph-wrapper')).offset().top) { // if position menu is overflow bottom
39                 menuPosition.y = document.body.scrollHeight - Sdc.Utils.Constants.GraphUIObjects.TOP_HEADER_HEIGHT - Sdc.Utils.Constants.GraphUIObjects.LINK_MENU_HEIGHT;
40             }
41             return menuPosition;
42         };
43
44
45         /**
46          * will return true/false if two nodes overlapping
47          *
48          * @param graph node
49          */
50         private isNodesOverlapping(node: Cy.CollectionFirstNode, draggedNode: Cy.CollectionFirstNode): boolean {
51
52             let nodeBoundingBox: Cy.BoundingBox = node.renderedBoundingBox();
53             let secondNodeBoundingBox: Cy.BoundingBox = draggedNode.renderedBoundingBox();
54
55             return this.isBBoxOverlapping(nodeBoundingBox, secondNodeBoundingBox);
56         }
57
58         /**
59          * Checks whether the bounding boxes of two nodes are overlapping on any side
60          * @param nodeOneBBox
61          * @param nodeTwoBBox
62          * @returns {boolean}
63          */
64         private isBBoxOverlapping(nodeOneBBox: Cy.BoundingBox, nodeTwoBBox: Cy.BoundingBox) {
65             return (((nodeOneBBox.x1 < nodeTwoBBox.x1 && nodeOneBBox.x2 > nodeTwoBBox.x1) ||
66             (nodeOneBBox.x1 < nodeTwoBBox.x2 && nodeOneBBox.x2 > nodeTwoBBox.x2) ||
67             (nodeTwoBBox.x1 < nodeOneBBox.x1 && nodeTwoBBox.x2 > nodeOneBBox.x2)) &&
68             ((nodeOneBBox.y1 < nodeTwoBBox.y1 && nodeOneBBox.y2 > nodeTwoBBox.y1) ||
69             (nodeOneBBox.y1 < nodeTwoBBox.y2 && nodeOneBBox.y2 > nodeTwoBBox.y2) ||
70             (nodeTwoBBox.y1 < nodeOneBBox.y1 && nodeTwoBBox.y2 > nodeOneBBox.y2)))
71         }
72
73
74         /**
75          * Checks whether a specific component instance can be hosted on the UCPE instance
76          * @param cy - Cytoscape instance
77          * @param fromUcpeInstance
78          * @param toComponentInstance
79          * @returns {Models.MatchReqToCapability}
80          */
81         public canBeHostedOn(cy: Cy.Instance, fromUcpeInstance: Models.ComponentsInstances.ComponentInstance, toComponentInstance: Models.ComponentsInstances.ComponentInstance): Models.MatchReqToCapability {
82
83             let matches: Array<Models.MatchBase> = this.matchCapabilitiesRequirementsUtils.getMatchedRequirementsCapabilities(fromUcpeInstance, toComponentInstance, this.getAllCompositionCiLinks(cy));
84             let hostedOnMatch: Models.MatchBase = _.find(matches, (match: Models.MatchReqToCapability) => {
85                 return match.requirement.capability.toLowerCase() === 'tosca.capabilities.container';
86             });
87
88             return <Models.MatchReqToCapability>hostedOnMatch;
89         };
90
91
92         /**
93          * Checks whether node can be dropped into UCPE
94          * @param cy
95          * @param nodeToInsert
96          * @param ucpeNode
97          * @returns {boolean}
98          */
99         private isValidDropInsideUCPE(cy: Cy.Instance, nodeToInsert: Models.ComponentsInstances.ComponentInstance, ucpeNode:  Models.ComponentsInstances.ComponentInstance): boolean {
100
101                 let hostedOnMatch: Models.MatchReqToCapability = this.canBeHostedOn(cy, ucpeNode, nodeToInsert);
102                 let result: boolean = !angular.isUndefined(hostedOnMatch) || nodeToInsert.isVl(); //group validation
103                 return result;
104
105         };
106
107
108         /**
109          * For drops from palette, checks whether the node can be dropped. If node is being held over another node, check if capable of hosting
110          * @param cy
111          * @param pseudoNodeBBox
112          * @param paletteComponentInstance
113          * @returns {boolean}
114          */
115         public isPaletteDropValid(cy: Cy.Instance, pseudoNodeBBox: Cy.BoundingBox, paletteComponentInstance:Sdc.Models.ComponentsInstances.ComponentInstance) {
116
117             let componentIsUCPE:boolean = (paletteComponentInstance.capabilities && paletteComponentInstance.capabilities['tosca.capabilities.Container'] && paletteComponentInstance.name.toLowerCase().indexOf('ucpe') > -1);
118
119             if(componentIsUCPE && cy.nodes('[?isUcpe]').length > 0)  { //second UCPE not allowed
120                 return false;
121             }
122
123             let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode: Cy.CollectionFirstNode) => {
124
125                 if(this.isBBoxOverlapping(pseudoNodeBBox, graphNode.renderedBoundingBox())){
126                     if (!componentIsUCPE && graphNode.data().isUcpe) {
127                         return !this.isValidDropInsideUCPE(cy, paletteComponentInstance, graphNode.data().componentInstance); //if this is valid insert into ucpe, we return false - no illegal overlapping nodes
128                     }
129                     return true;
130                 }
131
132                 return false;
133             });
134
135             return illegalOverlappingNodes.length === 0;
136         }
137
138         /**
139          * will return true/false if a drop of a single node is valid
140          *
141          * @param graph node
142          */
143         public isValidDrop(cy: Cy.Instance, draggedNode: Cy.CollectionFirstNode): boolean {
144
145             let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode: Cy.CollectionFirstNode) => { //all sdc nodes, removing child nodes (childe node allways collaps
146
147                 if (draggedNode.data().isUcpe && (graphNode.isChild() || graphNode.data().isInsideGroup)) { //ucpe cps always inside ucpe, no overlapping
148                     return false;
149                 }
150                 if(draggedNode.data().isInsideGroup && (!draggedNode.active() || graphNode.data().isUcpe)) {
151                     return false;
152                 }
153
154                 if (!draggedNode.data().isUcpe && !(draggedNode.data() instanceof Sdc.Models.Graph.CompositionCiNodeUcpeCp) && graphNode.data().isUcpe) { //case we are dragging a node into UCPE
155                     let isEntirelyInUCPE:boolean = this.commonGraphUtils.isFirstBoxContainsInSecondBox(draggedNode.renderedBoundingBox(), graphNode.renderedBoundingBox());
156                     if (isEntirelyInUCPE){
157                         if(this.isValidDropInsideUCPE(cy, draggedNode.data().componentInstance, graphNode.data().componentInstance)){ //if this is valid insert into ucpe, we return false - no illegal overlapping nodes
158                             return false;
159                         }
160                     }
161                 }
162                 return graphNode.data().id !== draggedNode.data().id && this.isNodesOverlapping(draggedNode, graphNode);
163
164             });
165             // return false;
166             return illegalOverlappingNodes.length === 0;
167         };
168
169         /**
170          * will return true/false if the move of the nodes is valid (no node overlapping and verifying if insert into UCPE is valid)
171          *
172          * @param  nodesArray - the selected drags nodes
173          */
174         public isGroupValidDrop(cy: Cy.Instance, nodesArray: Cy.CollectionNodes): boolean {
175             var filterDraggedNodes = nodesArray.filter('[?isDraggable]');
176             let isValidDrop = _.every(filterDraggedNodes, (node: Cy.CollectionFirstNode) => {
177                 return this.isValidDrop(cy, node);
178
179             });
180             return isValidDrop;
181         };
182
183         /**
184          * get all links in diagram
185          * @param cy
186          * @returns {any[]|boolean[]}
187          */
188         public getAllCompositionCiLinks = (cy: Cy.Instance): Array<Models.CompositionCiLinkBase> => {
189             return _.map(cy.edges("[isSdcElement]"), (edge: Cy.CollectionEdges) => {
190                 return edge.data();
191             });
192         };
193
194
195         /**
196          * Get Graph Utils server queue
197          * @returns {Sdc.Utils.Functions.QueueUtils}
198          */
199         public getGraphUtilsServerUpdateQueue(): Sdc.Utils.Functions.QueueUtils {
200             return CompositionGraphGeneralUtils.graphUtilsUpdateQueue;
201         }
202         ;
203
204         /**
205          *
206          * @param blockAction - true/false if this is a block action
207          * @param instances
208          * @param component
209          */
210         public pushMultipleUpdateComponentInstancesRequestToQueue = (blockAction: boolean, instances: Array<Models.ComponentsInstances.ComponentInstance>, component: Models.Components.Component): void => {
211             if (blockAction) {
212                 this.getGraphUtilsServerUpdateQueue().addBlockingUIAction(
213                     () => component.updateMultipleComponentInstances(instances)
214                 );
215             } else {
216                 this.getGraphUtilsServerUpdateQueue().addNonBlockingUIAction(
217                     () => component.updateMultipleComponentInstances(instances),
218                     () => this.LoaderService.hideLoader('composition-graph'));
219             }
220         };
221
222         /**
223          * this function will update component instance data
224          * @param blockAction - true/false if this is a block action
225          * @param updatedInstance
226          */
227         public pushUpdateComponentInstanceActionToQueue = (component: Models.Components.Component, blockAction: boolean, updatedInstance: Models.ComponentsInstances.ComponentInstance): void => {
228
229             if (blockAction) {
230                 this.LoaderService.showLoader('composition-graph');
231                 this.getGraphUtilsServerUpdateQueue().addBlockingUIAction(
232                     () => component.updateComponentInstance(updatedInstance)
233                 );
234             } else {
235                 this.getGraphUtilsServerUpdateQueue().addNonBlockingUIAction(
236                     () => component.updateComponentInstance(updatedInstance),
237                     () => this.LoaderService.hideLoader('composition-graph'));
238             }
239         };
240     }
241
242     CompositionGraphGeneralUtils.$inject = ['$q', 'LoaderService',  'CommonGraphUtils', 'MatchCapabilitiesRequirementsUtils'];
243 }