diff options
author | Michael Lando <ml636r@att.com> | 2017-06-09 03:19:04 +0300 |
---|---|---|
committer | Michael Lando <ml636r@att.com> | 2017-06-09 03:19:04 +0300 |
commit | ed64b5edff15e702493df21aa3230b81593e6133 (patch) | |
tree | a4cb01fdaccc34930a8db403a3097c0d1e40914b /catalog-ui/src/app/directives/graphs-v2/common | |
parent | 280f8015d06af1f41a3ef12e8300801c7a5e0d54 (diff) |
[SDC-29] catalog 1707 rebase commit.
Change-Id: I43c3dc5cf44abf5da817649bc738938a3e8388c1
Signed-off-by: Michael Lando <ml636r@att.com>
Diffstat (limited to 'catalog-ui/src/app/directives/graphs-v2/common')
3 files changed, 711 insertions, 0 deletions
diff --git a/catalog-ui/src/app/directives/graphs-v2/common/common-graph-utils.ts b/catalog-ui/src/app/directives/graphs-v2/common/common-graph-utils.ts new file mode 100644 index 0000000000..0b02173e9a --- /dev/null +++ b/catalog-ui/src/app/directives/graphs-v2/common/common-graph-utils.ts @@ -0,0 +1,372 @@ +import {CommonNodeBase, CompositionCiLinkBase, RelationshipModel, Relationship, CompositionCiNodeBase, NodesFactory, LinksFactory} from "app/models"; +import {GraphUIObjects} from "app/utils"; +/** + * Created by obarda on 12/21/2016. + */ +export class CommonGraphUtils { + + constructor(private NodesFactory:NodesFactory, private LinksFactory:LinksFactory) { + + } + + public safeApply = (scope:ng.IScope, fn:any) => { //todo remove to general utils + let phase = scope.$root.$$phase; + if (phase == '$apply' || phase == '$digest') { + if (fn && (typeof(fn) === 'function')) { + fn(); + } + } else { + scope.$apply(fn); + } + }; + + /** + * Draw node on the graph + * @param cy + * @param compositionGraphNode + * @param position + * @returns {CollectionElements} + */ + public addNodeToGraph(cy:Cy.Instance, compositionGraphNode:CommonNodeBase, position?:Cy.Position):Cy.CollectionElements { + + let node = cy.add(<Cy.ElementDefinition> { + group: 'nodes', + position: position, + data: compositionGraphNode, + classes: compositionGraphNode.classes + }); + + if(!node.data().isUcpe) { //ucpe should not have tooltip + this.initNodeTooltip(node); + } + return node; + }; + + /** + * The function will create a component instance node by the componentInstance position. + * If the node is UCPE the function will create all cp lan&wan for the ucpe + * @param cy + * @param compositionGraphNode + * @returns {Cy.CollectionElements} + */ + public addComponentInstanceNodeToGraph(cy:Cy.Instance, compositionGraphNode:CompositionCiNodeBase):Cy.CollectionElements { + + let nodePosition = { + x: +compositionGraphNode.componentInstance.posX, + y: +compositionGraphNode.componentInstance.posY + }; + + let node = this.addNodeToGraph(cy, compositionGraphNode, nodePosition); + if (compositionGraphNode.isUcpe) { + this.createUcpeCpNodes(cy, node); + } + return node; + }; + + /** + * This function will create CP_WAN & CP_LAN for the UCPE. this is a special node on the group that will behave like ports on the ucpe + * @param cy + * @param ucpeGraphNode + */ + private createUcpeCpNodes(cy:Cy.Instance, ucpeGraphNode:Cy.CollectionNodes):void { + + let requirementsArray:Array<any> = ucpeGraphNode.data().componentInstance.requirements["tosca.capabilities.Node"]; + //show only LAN or WAN requirements + requirementsArray = _.reject(requirementsArray, (requirement:any) => { + let name:string = requirement.ownerName.toLowerCase(); + return name.indexOf('lan') === -1 && name.indexOf('wan') === -1; + }); + requirementsArray.sort(function (a, b) { + let nameA = a.ownerName.toLowerCase().match(/[^ ]+/)[0]; + let nameB = b.ownerName.toLowerCase().match(/[^ ]+/)[0]; + let numA = _.last(a.ownerName.toLowerCase().split(' ')); + let numB = _.last(b.ownerName.toLowerCase().split(' ')); + + if (nameA === nameB) return numA > numB ? 1 : -1; + return nameA < nameB ? 1 : -1; + }); + let position = angular.copy(ucpeGraphNode.boundingbox()); + //add CP nodes to group + let topCps:number = 0; + for (let i = 0; i < requirementsArray.length; i++) { + + let cpNode = this.NodesFactory.createUcpeCpNode(angular.copy(ucpeGraphNode.data().componentInstance)); + cpNode.componentInstance.capabilities = requirementsArray[i]; + cpNode.id = requirementsArray[i].ownerId; + cpNode.group = ucpeGraphNode.data().componentInstance.uniqueId; + cpNode.name = requirementsArray[i].ownerName; //for tooltip + cpNode.displayName = requirementsArray[i].ownerName; + cpNode.displayName = cpNode.displayName.length > 5 ? cpNode.displayName.substring(0, 5) + '...' : cpNode.displayName; + + + if (cpNode.name.toLowerCase().indexOf('lan') > -1) { + cpNode.textPosition = "top"; + cpNode.componentInstance.posX = position.x1 + (i * 90) - (topCps * 90) + 53; + cpNode.componentInstance.posY = position.y1 + 400 + 27; + } else { + cpNode.textPosition = "bottom"; + cpNode.componentInstance.posX = position.x1 + (topCps * 90) + 53; + cpNode.componentInstance.posY = position.y1 + 27; + topCps++; + } + let cyCpNode = this.addComponentInstanceNodeToGraph(cy, cpNode); + cyCpNode.lock(); + } + }; + + /** + * + * @param nodes - all nodes in graph in order to find the edge connecting the two nodes + * @param fromNodeId + * @param toNodeId + * @returns {boolean} true/false if the edge is certified (from node and to node are certified) + */ + public isRelationCertified(nodes:Cy.CollectionNodes, fromNodeId:string, toNodeId:string):boolean { + let resourceTemp = _.filter(nodes, function (node:Cy.CollectionFirst) { + return node.data().id === fromNodeId || node.data().id === toNodeId; + }); + let certified:boolean = true; + + _.forEach(resourceTemp, (item) => { + certified = certified && item.data().certified; + }); + + return certified; + } + + /** + * Add link to graph - only draw the link + * @param cy + * @param link + */ + public insertLinkToGraph = (cy:Cy.Instance, link:CompositionCiLinkBase) => { + + if (!this.isRelationCertified(cy.nodes(), link.source, link.target)) { + link.classes = 'not-certified-link'; + } + cy.add({ + group: 'edges', + data: link, + classes: link.classes + }); + + }; + + /** + * go over the relations and draw links on the graph + * @param cy + * @param instancesRelations + */ + public initGraphLinks(cy:Cy.Instance, instancesRelations:Array<RelationshipModel>) { + + if (instancesRelations) { + _.forEach(instancesRelations, (relationshipModel:RelationshipModel) => { + _.forEach(relationshipModel.relationships, (relationship:Relationship) => { + let linkToCreate = this.LinksFactory.createGraphLink(cy, relationshipModel, relationship); + this.insertLinkToGraph(cy, linkToCreate); + }); + }); + } + } + + /** + * Determine which nodes are in the UCPE and set child data for them. + * @param cy + */ + public initUcpeChildren(cy:Cy.Instance) { + let ucpe:Cy.CollectionNodes = cy.nodes('[?isUcpe]'); // Get ucpe on graph if exist + _.each(cy.edges('.ucpe-host-link'), (link)=> { + + let ucpeChild:Cy.CollectionNodes = (link.source().id() == ucpe.id()) ? link.target() : link.source(); + this.initUcpeChildData(ucpeChild, ucpe); + + //vls dont have ucpe-host-link connection, so need to find them and iterate separately + let connectedVLs = ucpeChild.connectedEdges().connectedNodes('.vl-node'); + _.forEach(connectedVLs, (vl)=> { //all connected vls must be UCPE children because not allowed to connect to a VL outside of the UCPE + this.initUcpeChildData(vl, ucpe); + }); + }); + } + + /** + * Set properties for nodes contained by the UCPE + * @param childNode- node contained in UCPE + * @param ucpe- ucpe container node + */ + public initUcpeChildData(childNode:Cy.CollectionNodes, ucpe:Cy.CollectionNodes) { + + if (!childNode.data('isInsideGroup')) { + this.updateUcpeChildPosition(childNode, ucpe); + childNode.data({isInsideGroup: true}); + } + + } + + /** + * Updates UCPE child node offset, which allows child nodes to be dragged in synchronization with ucpe + * @param childNode- node contained in UCPE + * @param ucpe- ucpe container node + */ + public updateUcpeChildPosition(childNode:Cy.CollectionNodes, ucpe:Cy.CollectionNodes) { + let childPos:Cy.Position = childNode.relativePosition(); + let ucpePos:Cy.Position = ucpe.relativePosition(); + let offset:Cy.Position = { + x: childPos.x - ucpePos.x, + y: childPos.y - ucpePos.y + }; + childNode.data("ucpeOffset", offset); + } + + /** + * Removes ucpe-child properties from the node + * @param childNode- node being removed from UCPE + */ + public removeUcpeChildData(childNode:Cy.CollectionNodes) { + childNode.removeData("ucpeOffset"); + childNode.data({isInsideGroup: false}); + + } + + + public HTMLCoordsToCytoscapeCoords(cytoscapeBoundingBox:Cy.Extent, mousePos:Cy.Position):Cy.Position { + return {x: mousePos.x + cytoscapeBoundingBox.x1, y: mousePos.y + cytoscapeBoundingBox.y1} + }; + + + public getCytoscapeNodePosition = (cy:Cy.Instance, event:IDragDropEvent):Cy.Position => { + let targetOffset = $(event.target).offset(); + let x = event.pageX - targetOffset.left; + let y = event.pageY - targetOffset.top; + + return this.HTMLCoordsToCytoscapeCoords(cy.extent(), { + x: x, + y: y + }); + }; + + + public getNodePosition(node:Cy.CollectionFirstNode):Cy.Position { + let nodePosition = node.relativePoint(); + if (node.data().isUcpe) { //UCPEs use bounding box and not relative point. + nodePosition = {x: node.boundingbox().x1, y: node.boundingbox().y1}; + } + + return nodePosition; + } + + /** + * Generic function that can be used for any html elements overlaid on canvas + * 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. + * @param node + * @param additionalOffset + * @returns {Cy.Position} + + public getNodePositionWithOffset = (node:Cy.CollectionFirstNode, additionalOffset?:Cy.Position): Cy.Position => { + if(!additionalOffset) additionalOffset = {x: 0, y:0}; + + let nodePosition = node.renderedPosition(); + let posWithOffset:Cy.Position = { + x: nodePosition.x + GraphUIObjects.DIAGRAM_PALETTE_WIDTH_OFFSET + additionalOffset.x, + y: nodePosition.y + GraphUIObjects.COMPOSITION_HEADER_OFFSET + additionalOffset.y + }; + return posWithOffset; + };*/ + + /** + * return true/false if first node contains in second - this used in order to verify is node is entirely inside ucpe + * @param firstBox + * @param secondBox + * @returns {boolean} + */ + public isFirstBoxContainsInSecondBox(firstBox:Cy.BoundingBox, secondBox:Cy.BoundingBox) { + + return firstBox.x1 > secondBox.x1 && firstBox.x2 < secondBox.x2 && firstBox.y1 > secondBox.y1 && firstBox.y2 < secondBox.y2; + + }; + + + /** + * Check if node node bounds position is inside any ucpe on graph, and return the ucpe + * @param {diagram} the diagram. + * @param {nodeActualBounds} the actual bound position of the node. + * @return the ucpe if found else return null + */ + public isInUcpe = (cy:Cy.Instance, nodeBounds:Cy.BoundingBox):Cy.CollectionElements => { + + let ucpeNodes = cy.nodes('[?isUcpe]').filterFn((ucpeNode) => { + return this.isFirstBoxContainsInSecondBox(nodeBounds, ucpeNode.boundingbox()); + }); + return ucpeNodes; + }; + + /** + * + * @param cy + * @param node + * @returns {Array} + */ + public getLinkableNodes(cy:Cy.Instance, node:Cy.CollectionFirstNode):Array<CompositionCiNodeBase> { + let compatibleNodes = []; + _.each(cy.nodes(), (tempNode)=> { + if (this.nodeLocationsCompatible(cy, node, tempNode)) { + compatibleNodes.push(tempNode.data()); + } + }); + return compatibleNodes; + } + + /** + * Checks whether node locations are compatible in reference to UCPEs. + * Returns true if both nodes are in UCPE or both nodes out, or one node is UCPEpart. + * @param node1 + * @param node2 + */ + public nodeLocationsCompatible(cy:Cy.Instance, node1:Cy.CollectionFirstNode, node2:Cy.CollectionFirstNode) { + + let ucpe = cy.nodes('[?isUcpe]'); + if(!ucpe.length){ return true; } + if(node1.data().isUcpePart || node2.data().isUcpePart) { return true; } + + return (this.isFirstBoxContainsInSecondBox(node1.boundingbox(), ucpe.boundingbox()) == this.isFirstBoxContainsInSecondBox(node2.boundingbox(), ucpe.boundingbox())); + + } + + /** + * This function will init qtip tooltip on the node + * @param node - the node we want the tooltip to apply on + */ + public initNodeTooltip(node:Cy.CollectionNodes) { + + let opts = { + content: function () { + return this.data('name'); + }, + position: { + my: 'top center', + at: 'bottom center', + adjust: {x:0, y:-5} + }, + style: { + classes: 'qtip-dark qtip-rounded qtip-custom', + tip: { + width: 16, + height: 8 + } + }, + show: { + event: 'mouseover', + delay: 1000 + }, + hide: {event: 'mouseout mousedown'}, + includeLabels: true + }; + + if (node.data().isUcpePart){ //fix tooltip positioning for UCPE-cps + opts.position.adjust = {x:0, y:20}; + } + + node.qtip(opts); + }; +} + +CommonGraphUtils.$inject = ['NodesFactory', 'LinksFactory'];
\ No newline at end of file diff --git a/catalog-ui/src/app/directives/graphs-v2/common/style/component-instances-nodes-style.ts b/catalog-ui/src/app/directives/graphs-v2/common/style/component-instances-nodes-style.ts new file mode 100644 index 0000000000..971dabafe8 --- /dev/null +++ b/catalog-ui/src/app/directives/graphs-v2/common/style/component-instances-nodes-style.ts @@ -0,0 +1,256 @@ +import {GraphColors} from "app/utils/constants"; +/** + * Created by obarda on 12/18/2016. + */ +export class ComponentInstanceNodesStyle { + + public static getCompositionGraphStyle = ():Array<Cy.Stylesheet> => { + return [ + { + selector: 'core', + css: { + 'shape': 'rectangle', + 'active-bg-size': 0, + 'selection-box-color': 'rgb(0, 159, 219)', + 'selection-box-opacity': 0.2, + 'selection-box-border-color': '#009fdb', + 'selection-box-border-width': 1 + + } + }, + { + selector: 'node', + css: { + 'font-family': 'omnes-regular,sans-serif', + 'font-size': 14, + 'events': 'yes', + 'text-events': 'yes', + 'text-border-width': 15, + 'text-border-color': GraphColors.NODE_UCPE, + 'text-margin-y': 5 + } + }, + { + selector: '.vf-node', + css: { + 'background-color': 'transparent', + 'shape': 'rectangle', + 'label': 'data(displayName)', + 'background-image': 'data(img)', + 'width': 65, + 'height': 65, + 'background-opacity': 0, + "background-width": 65, + "background-height": 65, + 'text-valign': 'bottom', + 'text-halign': 'center', + 'background-fit': 'cover', + 'background-clip': 'node', + 'overlay-color': GraphColors.NODE_BACKGROUND_COLOR, + 'overlay-opacity': 0 + } + }, + + { + selector: '.service-node', + css: { + 'background-color': 'transparent', + 'label': 'data(displayName)', + 'events': 'yes', + 'text-events': 'yes', + 'background-image': 'data(img)', + 'width': 64, + 'height': 64, + "border-width": 0, + 'text-valign': 'bottom', + 'text-halign': 'center', + 'background-opacity': 0, + 'overlay-color': GraphColors.NODE_BACKGROUND_COLOR, + 'overlay-opacity': 0 + } + }, + { + selector: '.cp-node', + css: { + 'background-color': 'rgb(255,255,255)', + 'shape': 'rectangle', + 'label': 'data(displayName)', + 'background-image': 'data(img)', + 'background-width': 21, + 'background-height': 21, + 'width': 21, + 'height': 21, + 'text-valign': 'bottom', + 'text-halign': 'center', + 'background-opacity': 0, + 'overlay-color': GraphColors.NODE_BACKGROUND_COLOR, + 'overlay-opacity': 0 + } + }, + { + selector: '.vl-node', + css: { + 'background-color': 'rgb(255,255,255)', + 'shape': 'rectangle', + 'label': 'data(displayName)', + 'background-image': 'data(img)', + 'background-width': 21, + 'background-height': 21, + 'width': 21, + 'height': 21, + 'text-valign': 'bottom', + 'text-halign': 'center', + 'background-opacity': 0, + 'overlay-color': GraphColors.NODE_BACKGROUND_COLOR, + 'overlay-opacity': 0 + } + }, + { + selector: '.ucpe-cp', + css: { + 'background-color': GraphColors.NODE_UCPE_CP, + 'background-width': 15, + 'background-height': 15, + 'width': 15, + 'height': 15, + 'text-halign': 'center', + 'overlay-opacity': 0, + 'label': 'data(displayName)', + 'text-valign': 'data(textPosition)', + 'text-margin-y': (ele:Cy.Collection) => { + return (ele.data('textPosition') == 'top') ? -5 : 5; + }, + 'font-size': 12 + } + }, + { + selector: '.ucpe-node', + css: { + 'background-fit': 'cover', + 'padding-bottom': 0, + 'padding-top': 0 + } + }, + { + selector: '.simple-link', + css: { + 'width': 1, + 'line-color': GraphColors.BASE_LINK, + 'target-arrow-color': '#3b7b9b', + 'target-arrow-shape': 'triangle', + 'curve-style': 'bezier', + 'control-point-step-size': 30 + } + }, + { + selector: '.vl-link', + css: { + 'width': 3, + 'line-color': GraphColors.VL_LINK, + 'curve-style': 'bezier', + 'control-point-step-size': 30 + } + }, + { + selector: '.ucpe-host-link', + css: { + 'width': 0 + } + }, + { + selector: '.not-certified-link', + css: { + 'width': 1, + 'line-color': GraphColors.NOT_CERTIFIED_LINK, + 'curve-style': 'bezier', + 'control-point-step-size': 30, + 'line-style': 'dashed', + 'target-arrow-color': '#3b7b9b', + 'target-arrow-shape': 'triangle' + + } + }, + + { + selector: '.not-certified', + css: { + 'shape': 'rectangle', + 'background-image': (ele:Cy.Collection) => { + return ele.data().initImage(ele) + }, + "border-width": 0 + } + }, + { + selector: 'node:selected', + css: { + "border-width": 2, + "border-color": GraphColors.NODE_SELECTED_BORDER_COLOR, + 'shape': 'rectangle' + } + }, + { + selector: 'edge:selected', + css: { + 'line-color': GraphColors.ACTIVE_LINK + + } + }, + { + selector: 'edge:active', + css: { + 'overlay-opacity': 0 + } + } + ] + } + + public static getBasicNodeHanlde = () => { + return { + positionX: "center", + positionY: "top", + offsetX: 15, + offsetY: -20, + color: "#27a337", + type: "default", + single: false, + nodeTypeNames: ["basic-node"], + imageUrl: '/assets/styles/images/resource-icons/' + 'canvasPlusIcon.png', + lineWidth: 2, + lineStyle: 'dashed' + + } + } + + public static getBasicSmallNodeHandle = () => { + return { + positionX: "center", + positionY: "top", + offsetX: 3, + offsetY: -25, + color: "#27a337", + type: "default", + single: false, + nodeTypeNames: ["basic-small-node"], + imageUrl: '/assets/styles/images/resource-icons/' + 'canvasPlusIcon.png', + lineWidth: 2, + lineStyle: 'dashed' + } + } + + public static getUcpeCpNodeHandle = () => { + return { + positionX: "center", + positionY: "center", + offsetX: -8, + offsetY: -10, + color: "#27a337", + type: "default", + single: false, + nodeTypeNames: ["ucpe-cp-node"], + imageUrl: '/assets/styles/images/resource-icons/' + 'canvasPlusIcon.png', + lineWidth: 2, + lineStyle: 'dashed' + } + } +} diff --git a/catalog-ui/src/app/directives/graphs-v2/common/style/module-node-style.ts b/catalog-ui/src/app/directives/graphs-v2/common/style/module-node-style.ts new file mode 100644 index 0000000000..0c92c90538 --- /dev/null +++ b/catalog-ui/src/app/directives/graphs-v2/common/style/module-node-style.ts @@ -0,0 +1,83 @@ +import {GraphColors} from "app/utils"; +export class ModulesNodesStyle { + + public static getModuleGraphStyle = ():Array<Cy.Stylesheet> => { + + return [ + { + selector: '.cy-expand-collapse-collapsed-node', + css: { + 'background-image': 'data(img)', + 'width': 34, + 'height': 32, + 'background-opacity': 0, + 'shape': 'rectangle', + 'label': 'data(displayName)', + 'events': 'yes', + 'text-events': 'yes', + 'text-valign': 'bottom', + 'text-halign': 'center', + 'text-margin-y': 5, + 'border-opacity': 0 + } + }, + { + selector: '.module-node', + css: { + 'background-color': 'transparent', + 'background-opacity': 0, + "border-width": 2, + "border-color": GraphColors.NODE_SELECTED_BORDER_COLOR, + 'border-style': 'dashed', + 'label': 'data(displayName)', + 'events': 'yes', + 'text-events': 'yes', + 'text-valign': 'bottom', + 'text-halign': 'center', + 'text-margin-y': 8 + } + }, + { + selector: 'node:selected', + css: { + "border-opacity": 0 + } + }, + { + selector: '.simple-link:selected', + css: { + 'line-color': GraphColors.BASE_LINK, + } + }, + { + selector: '.vl-link:selected', + css: { + 'line-color': GraphColors.VL_LINK, + } + }, + { + selector: '.cy-expand-collapse-collapsed-node:selected', + css: { + "border-color": GraphColors.NODE_SELECTED_BORDER_COLOR, + 'border-opacity': 1, + 'border-style': 'solid', + 'border-width': 2 + } + }, + { + selector: '.module-node:selected', + css: { + "border-color": GraphColors.NODE_SELECTED_BORDER_COLOR, + 'border-opacity': 1 + } + }, + { + selector: '.dummy-node', + css: { + 'width': 20, + 'height': 20 + } + }, + ] + } +} |