Catalog alignment
[sdc.git] / catalog-ui / src / app / ng2 / pages / composition / graph / common / common-graph-utils.ts
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
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
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
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=========================================================
19  */
20
21 import * as _ from "lodash";
22 import {
23     CommonNodeBase,
24     Relationship,
25     CompositionCiNodeBase
26 } from "app/models";
27 import {CompositionCiServicePathLink} from "app/models/graph/graph-links/composition-graph-links/composition-ci-service-path-link";
28 import {Requirement, Capability} from "app/models";
29 import {Injectable} from "@angular/core";
30
31
32
33 @Injectable()
34 export class CommonGraphUtils {
35
36     constructor() {
37
38     }
39
40     public safeApply = (scope:ng.IScope, fn:any) => { //todo remove to general utils
41         let phase = scope.$root.$$phase;
42         if (phase == '$apply' || phase == '$digest') {
43             if (fn && (typeof(fn) === 'function')) {
44                 fn();
45             }
46         } else {
47             scope.$apply(fn);
48         }
49     };
50
51     /**
52      * Draw node on the graph
53      * @param cy
54      * @param compositionGraphNode
55      * @param position
56      * @returns {CollectionElements}
57      */
58     public addNodeToGraph(cy:Cy.Instance, compositionGraphNode:CommonNodeBase, position?:Cy.Position):Cy.CollectionElements {
59
60         let node = cy.add(<Cy.ElementDefinition> {
61             group: 'nodes',
62             position: position,
63             data: compositionGraphNode,
64             classes: compositionGraphNode.classes
65         });
66
67         this.initNodeTooltip(node);
68         return node;
69     };
70
71     /**
72      * The function will create a component instance node by the componentInstance position.
73      * If the node is UCPE the function will create all cp lan&wan for the ucpe
74      * @param cy
75      * @param compositionGraphNode
76      * @returns {Cy.CollectionElements}
77      */
78     public addComponentInstanceNodeToGraph(cy:Cy.Instance, compositionGraphNode:CompositionCiNodeBase):Cy.CollectionElements {
79
80         let nodePosition = {
81             x: +compositionGraphNode.componentInstance.posX,
82             y: +compositionGraphNode.componentInstance.posY
83         };
84
85         let node = this.addNodeToGraph(cy, compositionGraphNode, nodePosition);
86         return node;
87     };
88
89     /**
90      * Add service path link to graph - only draw the link
91      * @param cy
92      * @param link
93      */
94     public insertServicePathLinkToGraph = (cy:Cy.Instance, link:CompositionCiServicePathLink) => {
95         let linkElement = cy.add({
96             group: 'edges',
97             data: link,
98             classes: link.classes
99         });
100         this.initServicePathTooltip(linkElement, link);
101     };
102
103     /**
104      * Returns function for the link tooltip content
105      * @param {Relationship} linkRelation
106      * @param {Requirement} requirement
107      * @param {Capability} capability
108      * @returns {() => string}
109      * @private
110      */
111     private _getLinkTooltipContent(linkRelation:Relationship, requirement?:Requirement, capability?:Capability):string {
112         return '<div class="line">' +
113             '<span class="req-cap-label">R: </span>' +
114             '<span>' + (requirement ? requirement.getTitle() : linkRelation.relation.requirement) + '</span>' +
115             '</div>' +
116             '<div class="line">' +
117             '<div class="sprite-new link-tooltip-arrow"></div>' +
118             '<span class="req-cap-label">C: </span>' +
119             '<span>' + (capability ? capability.getTitle() : linkRelation.relation.capability) + '</span>' +
120             '</div>';
121     }
122
123     /**
124      * This function will init qtip tooltip on the link
125      * @param linkElement - the link we want the tooltip to apply on,
126      * @param link
127      * @param getLinkRequirementCapability
128      * link - the link obj
129      */
130     public initLinkTooltip(linkElement:Cy.CollectionElements, link:Relationship, getLinkRequirementCapability:Function) {
131         const content = () => this._getLinkTooltipContent(link);  // base tooltip content without owner names
132         const render = (event, api) => {
133             // on render (called once at first show), get the link requirement and capability and change to full tooltip content (with owner names)
134             getLinkRequirementCapability().then((linkReqCap) => {
135                 const fullContent = () => this._getLinkTooltipContent(link, linkReqCap.requirement, linkReqCap.capability);
136                 api.set('content.text', fullContent);
137             });
138         };
139         linkElement.qtip(this.prepareInitTooltipData({content, events: {render}}));
140     };
141
142     /**
143      *
144      * @param linkElement
145      * @param link
146      */
147     public initServicePathTooltip(linkElement:Cy.CollectionElements, link:CompositionCiServicePathLink) {
148         let content = function () {
149             return '<div class="line">' +
150                 '<div>' + link.pathName + '</div>' +
151                 '</div>';
152         };
153         linkElement.qtip(this.prepareInitTooltipData({content}));
154     };
155
156     private prepareInitTooltipData(options?:Object) {
157         return _.merge({
158             position: {
159                 my: 'top center',
160                 at: 'bottom center',
161                 adjust: {x: 0, y: 0},
162                 effect: false
163             },
164             style: {
165                 classes: 'qtip-dark qtip-rounded qtip-custom link-qtip',
166                 tip: {
167                     width: 16,
168                     height: 8
169                 }
170             },
171             show: {
172                 event: 'mouseover',
173                 delay: 1000
174             },
175             hide: {event: 'mouseout mousedown'},
176             includeLabels: true,
177             events: {}
178         }, options);
179     }
180
181     public HTMLCoordsToCytoscapeCoords(cytoscapeBoundingBox:Cy.Extent, mousePos:Cy.Position):Cy.Position {
182         return {x: mousePos.x + cytoscapeBoundingBox.x1, y: mousePos.y + cytoscapeBoundingBox.y1}
183     };
184
185
186     public getCytoscapeNodePosition = (cy:Cy.Instance, event:DragEvent | MouseEvent):Cy.Position => {
187         let targetOffset = $(event.target).offset();
188         if(event instanceof DragEvent) {
189             targetOffset = $('canvas').offset();
190         }
191         
192         let x = (event.pageX - targetOffset.left) / cy.zoom();
193         let y = (event.pageY - targetOffset.top) / cy.zoom();
194
195         return this.HTMLCoordsToCytoscapeCoords(cy.extent(), {
196             x: x,
197             y: y
198         });
199     };
200
201
202     public getNodePosition(node:Cy.CollectionFirstNode):Cy.Position {
203         let nodePosition = node.relativePoint();
204         if (node.data().isUcpe) { //UCPEs use bounding box and not relative point.
205             nodePosition = {x: node.boundingbox().x1, y: node.boundingbox().y1};
206         }
207
208         return nodePosition;
209     }
210
211     /**
212      * Generic function that can be used for any html elements overlaid on canvas
213      * Returns the html position of a node on canvas, including left palette and header offsets. Option to pass in additional offset to add to return position.
214      * @param node
215      * @param additionalOffset
216      * @returns {Cy.Position}
217
218      public getNodePositionWithOffset = (node:Cy.CollectionFirstNode, additionalOffset?:Cy.Position): Cy.Position => {
219             if(!additionalOffset) additionalOffset = {x: 0, y:0};
220
221             let nodePosition = node.renderedPosition();
222             let posWithOffset:Cy.Position = {
223                 x: nodePosition.x + GraphUIObjects.DIAGRAM_PALETTE_WIDTH_OFFSET + additionalOffset.x,
224                 y: nodePosition.y + GraphUIObjects.COMPOSITION_HEADER_OFFSET + additionalOffset.y
225             };
226             return posWithOffset;
227         };*/
228
229     /**
230      *  return true/false if first node contains in second - this used in order to verify is node is entirely inside ucpe
231      * @param firstBox
232      * @param secondBox
233      * @returns {boolean}
234      */
235     public isFirstBoxContainsInSecondBox(firstBox:Cy.BoundingBox, secondBox:Cy.BoundingBox) {
236
237         return firstBox.x1 > secondBox.x1 && firstBox.x2 < secondBox.x2 && firstBox.y1 > secondBox.y1 && firstBox.y2 < secondBox.y2;
238
239     };
240
241     /**
242      *
243      * @param cy
244      * @param node
245      * @returns {Array}
246      */
247     public getLinkableNodes(cy:Cy.Instance, node:Cy.CollectionFirstNode):Array<CompositionCiNodeBase> {
248         let compatibleNodes = [];
249         _.each(cy.nodes(), (tempNode)=> {
250             if (this.nodeLocationsCompatible(node, tempNode)) {
251                 compatibleNodes.push(tempNode.data());
252             }
253         });
254         return compatibleNodes;
255     }
256
257     /**
258      * Checks whether node locations are compatible in reference to UCPEs.
259      * Returns true if both nodes are in UCPE or both nodes out, or one node is UCPEpart.
260      * @param node1
261      * @param node2
262      */
263     public nodeLocationsCompatible(node1:Cy.CollectionFirstNode, node2:Cy.CollectionFirstNode) {
264         return (this.isFirstBoxContainsInSecondBox(node1.boundingbox(), node2.boundingbox()));
265     }
266
267     /**
268      * This function will init qtip tooltip on the node
269      * @param node - the node we want the tooltip to apply on
270      */
271     public initNodeTooltip(node:Cy.CollectionNodes) {
272
273         let opts = {
274             content: function () {
275                 return this.data('name');
276             },
277             position: {
278                 my: 'top center',
279                 at: 'bottom center',
280                 adjust: {x: 0, y: -5}
281             },
282             style: {
283                 classes: 'qtip-dark qtip-rounded qtip-custom',
284                 tip: {
285                     width: 16,
286                     height: 8
287                 }
288             },
289             show: {
290                 event: 'mouseover',
291                 delay: 1000
292             },
293             hide: {event: 'mouseout mousedown'},
294             includeLabels: true
295         };
296
297         if (node.data().isUcpePart) { //fix tooltip positioning for UCPE-cps
298             opts.position.adjust = {x: 0, y: 20};
299         }
300
301         node.qtip(opts);
302     };
303 }
304