///
module Sdc.Graph.Utils {
import Dictionary = Sdc.Utils.Dictionary;
export class CompositionGraphGeneralUtils {
public componentRequirementsAndCapabilitiesCaching = new Dictionary();
protected static graphUtilsUpdateQueue: Sdc.Utils.Functions.QueueUtils;
constructor(private $q: ng.IQService,
private LoaderService: Services.LoaderService,
private commonGraphUtils: Sdc.Graph.Utils.CommonGraphUtils,
private matchCapabilitiesRequirementsUtils: Graph.Utils.MatchCapabilitiesRequirementsUtils) {
CompositionGraphGeneralUtils.graphUtilsUpdateQueue = new Sdc.Utils.Functions.QueueUtils(this.$q);
}
/**
* Get the offset for the link creation Menu
* @param point
* @returns {Cy.Position}
*/
public calcMenuOffset: Function = (point: Cy.Position): Cy.Position => {
point.x = point.x + 60;
point.y = point.y + 105;
return point;
};
/**
* return the top left position of the link menu
* @param cy
* @param targetNodePosition
* @returns {Cy.Position}
*/
public getLinkMenuPosition = (cy: Cy.Instance, targetNodePosition: Cy.Position) => {
let menuPosition: Cy.Position = this.calcMenuOffset(targetNodePosition); //get the link mid point
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
menuPosition.y = document.body.scrollHeight - Sdc.Utils.Constants.GraphUIObjects.TOP_HEADER_HEIGHT - Sdc.Utils.Constants.GraphUIObjects.LINK_MENU_HEIGHT;
}
return menuPosition;
};
/**
* will return true/false if two nodes overlapping
*
* @param graph node
*/
private isNodesOverlapping(node: Cy.CollectionFirstNode, draggedNode: Cy.CollectionFirstNode): boolean {
let nodeBoundingBox: Cy.BoundingBox = node.renderedBoundingBox();
let secondNodeBoundingBox: Cy.BoundingBox = draggedNode.renderedBoundingBox();
return this.isBBoxOverlapping(nodeBoundingBox, secondNodeBoundingBox);
}
/**
* Checks whether the bounding boxes of two nodes are overlapping on any side
* @param nodeOneBBox
* @param nodeTwoBBox
* @returns {boolean}
*/
private isBBoxOverlapping(nodeOneBBox: Cy.BoundingBox, nodeTwoBBox: Cy.BoundingBox) {
return (((nodeOneBBox.x1 < nodeTwoBBox.x1 && nodeOneBBox.x2 > nodeTwoBBox.x1) ||
(nodeOneBBox.x1 < nodeTwoBBox.x2 && nodeOneBBox.x2 > nodeTwoBBox.x2) ||
(nodeTwoBBox.x1 < nodeOneBBox.x1 && nodeTwoBBox.x2 > nodeOneBBox.x2)) &&
((nodeOneBBox.y1 < nodeTwoBBox.y1 && nodeOneBBox.y2 > nodeTwoBBox.y1) ||
(nodeOneBBox.y1 < nodeTwoBBox.y2 && nodeOneBBox.y2 > nodeTwoBBox.y2) ||
(nodeTwoBBox.y1 < nodeOneBBox.y1 && nodeTwoBBox.y2 > nodeOneBBox.y2)))
}
/**
* Checks whether a specific component instance can be hosted on the UCPE instance
* @param cy - Cytoscape instance
* @param fromUcpeInstance
* @param toComponentInstance
* @returns {Models.MatchReqToCapability}
*/
public canBeHostedOn(cy: Cy.Instance, fromUcpeInstance: Models.ComponentsInstances.ComponentInstance, toComponentInstance: Models.ComponentsInstances.ComponentInstance): Models.MatchReqToCapability {
let matches: Array = this.matchCapabilitiesRequirementsUtils.getMatchedRequirementsCapabilities(fromUcpeInstance, toComponentInstance, this.getAllCompositionCiLinks(cy));
let hostedOnMatch: Models.MatchBase = _.find(matches, (match: Models.MatchReqToCapability) => {
return match.requirement.capability.toLowerCase() === 'tosca.capabilities.container';
});
return hostedOnMatch;
};
/**
* Checks whether node can be dropped into UCPE
* @param cy
* @param nodeToInsert
* @param ucpeNode
* @returns {boolean}
*/
private isValidDropInsideUCPE(cy: Cy.Instance, nodeToInsert: Models.ComponentsInstances.ComponentInstance, ucpeNode: Models.ComponentsInstances.ComponentInstance): boolean {
let hostedOnMatch: Models.MatchReqToCapability = this.canBeHostedOn(cy, ucpeNode, nodeToInsert);
let result: boolean = !angular.isUndefined(hostedOnMatch) || nodeToInsert.isVl(); //group validation
return result;
};
/**
* For drops from palette, checks whether the node can be dropped. If node is being held over another node, check if capable of hosting
* @param cy
* @param pseudoNodeBBox
* @param paletteComponentInstance
* @returns {boolean}
*/
public isPaletteDropValid(cy: Cy.Instance, pseudoNodeBBox: Cy.BoundingBox, paletteComponentInstance:Sdc.Models.ComponentsInstances.ComponentInstance) {
let componentIsUCPE:boolean = (paletteComponentInstance.capabilities && paletteComponentInstance.capabilities['tosca.capabilities.Container'] && paletteComponentInstance.name.toLowerCase().indexOf('ucpe') > -1);
if(componentIsUCPE && cy.nodes('[?isUcpe]').length > 0) { //second UCPE not allowed
return false;
}
let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode: Cy.CollectionFirstNode) => {
if(this.isBBoxOverlapping(pseudoNodeBBox, graphNode.renderedBoundingBox())){
if (!componentIsUCPE && graphNode.data().isUcpe) {
return !this.isValidDropInsideUCPE(cy, paletteComponentInstance, graphNode.data().componentInstance); //if this is valid insert into ucpe, we return false - no illegal overlapping nodes
}
return true;
}
return false;
});
return illegalOverlappingNodes.length === 0;
}
/**
* will return true/false if a drop of a single node is valid
*
* @param graph node
*/
public isValidDrop(cy: Cy.Instance, draggedNode: Cy.CollectionFirstNode): boolean {
let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode: Cy.CollectionFirstNode) => { //all sdc nodes, removing child nodes (childe node allways collaps
if (draggedNode.data().isUcpe && (graphNode.isChild() || graphNode.data().isInsideGroup)) { //ucpe cps always inside ucpe, no overlapping
return false;
}
if(draggedNode.data().isInsideGroup && (!draggedNode.active() || graphNode.data().isUcpe)) {
return false;
}
if (!draggedNode.data().isUcpe && !(draggedNode.data() instanceof Sdc.Models.Graph.CompositionCiNodeUcpeCp) && graphNode.data().isUcpe) { //case we are dragging a node into UCPE
let isEntirelyInUCPE:boolean = this.commonGraphUtils.isFirstBoxContainsInSecondBox(draggedNode.renderedBoundingBox(), graphNode.renderedBoundingBox());
if (isEntirelyInUCPE){
if(this.isValidDropInsideUCPE(cy, draggedNode.data().componentInstance, graphNode.data().componentInstance)){ //if this is valid insert into ucpe, we return false - no illegal overlapping nodes
return false;
}
}
}
return graphNode.data().id !== draggedNode.data().id && this.isNodesOverlapping(draggedNode, graphNode);
});
// return false;
return illegalOverlappingNodes.length === 0;
};
/**
* will return true/false if the move of the nodes is valid (no node overlapping and verifying if insert into UCPE is valid)
*
* @param nodesArray - the selected drags nodes
*/
public isGroupValidDrop(cy: Cy.Instance, nodesArray: Cy.CollectionNodes): boolean {
var filterDraggedNodes = nodesArray.filter('[?isDraggable]');
let isValidDrop = _.every(filterDraggedNodes, (node: Cy.CollectionFirstNode) => {
return this.isValidDrop(cy, node);
});
return isValidDrop;
};
/**
* get all links in diagram
* @param cy
* @returns {any[]|boolean[]}
*/
public getAllCompositionCiLinks = (cy: Cy.Instance): Array => {
return _.map(cy.edges("[isSdcElement]"), (edge: Cy.CollectionEdges) => {
return edge.data();
});
};
/**
* Get Graph Utils server queue
* @returns {Sdc.Utils.Functions.QueueUtils}
*/
public getGraphUtilsServerUpdateQueue(): Sdc.Utils.Functions.QueueUtils {
return CompositionGraphGeneralUtils.graphUtilsUpdateQueue;
}
;
/**
*
* @param blockAction - true/false if this is a block action
* @param instances
* @param component
*/
public pushMultipleUpdateComponentInstancesRequestToQueue = (blockAction: boolean, instances: Array, component: Models.Components.Component): void => {
if (blockAction) {
this.getGraphUtilsServerUpdateQueue().addBlockingUIAction(
() => component.updateMultipleComponentInstances(instances)
);
} else {
this.getGraphUtilsServerUpdateQueue().addNonBlockingUIAction(
() => component.updateMultipleComponentInstances(instances),
() => this.LoaderService.hideLoader('composition-graph'));
}
};
/**
* this function will update component instance data
* @param blockAction - true/false if this is a block action
* @param updatedInstance
*/
public pushUpdateComponentInstanceActionToQueue = (component: Models.Components.Component, blockAction: boolean, updatedInstance: Models.ComponentsInstances.ComponentInstance): void => {
if (blockAction) {
this.LoaderService.showLoader('composition-graph');
this.getGraphUtilsServerUpdateQueue().addBlockingUIAction(
() => component.updateComponentInstance(updatedInstance)
);
} else {
this.getGraphUtilsServerUpdateQueue().addNonBlockingUIAction(
() => component.updateComponentInstance(updatedInstance),
() => this.LoaderService.hideLoader('composition-graph'));
}
};
}
CompositionGraphGeneralUtils.$inject = ['$q', 'LoaderService', 'CommonGraphUtils', 'MatchCapabilitiesRequirementsUtils'];
}