UNPKG

@syncfusion/ej2-diagrams

Version:

Feature-rich diagram control to create diagrams like flow charts, organizational charts, mind maps, and BPMN diagrams. Its rich feature set includes built-in shapes, editing, serializing, exporting, printing, overview, data binding, and automatic layouts.

1,218 lines 151 kB
import { Size } from './../primitives/size'; import { Rect } from './../primitives/rect'; import { identityMatrix, rotateMatrix, transformPointByMatrix, scaleMatrix } from './../primitives/matrix'; import { DiagramElement } from './../core/elements/diagram-element'; import { GroupableView } from './../core/containers/container'; import { StrokeStyle, Stop } from './../core/appearance'; import { Point } from './../primitives/point'; import { ConnectorConstraints, NodeConstraints, PortConstraints, DiagramConstraints, DiagramTools, Transform, BlazorAction, ControlPointsVisibility, DiagramEvent, ElementAction } from './../enum/enum'; import { SelectorConstraints, ThumbsConstraints, FlipDirection } from './../enum/enum'; import { PathElement } from './../core/elements/path-element'; import { DiagramNativeElement } from './../core/elements/native-element'; import { TextElement } from '../core/elements/text-element'; import { ImageElement } from '../core/elements/image-element'; import { PathAnnotation, ShapeAnnotation } from './../objects/annotation'; import { Node, FlowShape, BasicShape, Native, Html, UmlActivityShape, BpmnGateway, BpmnDataObject, BpmnEvent, BpmnSubEvent, BpmnActivity, BpmnAnnotation, MethodArguments, UmlClassAttribute, UmlClassMethod, UmlClass, UmlInterface, UmlEnumerationMember, UmlEnumeration, Lane, Phase, ChildContainer, SwimLane, Path, Image, Text, BpmnShape, UmlClassifierShape, Header } from './../objects/node'; import { Connector, bezierPoints, BezierSegment, StraightSegment, OrthogonalSegment } from './../objects/connector'; import { getBasicShape } from './../objects/dictionary/basic-shapes'; import { getFlowShape } from './../objects/dictionary/flow-shapes'; import { Diagram } from './../diagram'; import { findAngle } from './connector'; import { getContent, removeElement, hasClass, getDiagramElement } from './dom-util'; import { getBounds, cloneObject, rotatePoint, getFunction, cornersPointsBeforeRotation, getOffset } from './base-util'; import { getPolygonPath } from './../utility/path-util'; import { DiagramHtmlElement } from '../core/elements/html-element'; import { getRulerSize } from '../ruler/ruler'; import { canResize } from './constraints-util'; import { UserHandle } from '../interaction/selector'; import { getUMLActivityShape } from '../objects/dictionary/umlactivity-shapes'; import { Canvas } from '../core/containers/canvas'; import { PointPort } from '../objects/port'; import { Command } from '../diagram/keyboard-commands'; import { pasteSwimLane } from './swim-lane-util'; import { isBlazor, Browser, isNullOrUndefined } from '@syncfusion/ej2-base'; import { ConnectorFixedUserHandle } from '../objects/fixed-user-handle'; import { Overview } from '../../overview/overview'; /** * completeRegion method\ * * @returns { void } completeRegion method .\ * @param {Rect} region - provide the region value. * @param {(NodeModel | ConnectorModel)[]} selectedObjects - provide the selectedObjects value. * @private */ export function completeRegion(region, selectedObjects) { var collection = []; for (var i = 0; i < selectedObjects.length; i++) { var obj = selectedObjects[parseInt(i.toString(), 10)]; if (region.containsRect(obj.wrapper.bounds)) { collection.push(obj); } } return collection; } /** * findNodeByName method \ * * @returns { boolean } findNodeByName method .\ * @param {(NodeModel | ConnectorModel)[]} nodes - provide the nodes value. * @param {string} name - provide the orientation value. * @private */ export function findNodeByName(nodes, name) { for (var i = 0; i < nodes.length; i++) { if (nodes[parseInt(i.toString(), 10)].id === name) { return true; } } return false; } /** * findNodeByName method \ * * @returns { string } findNodeByName method .\ * @param {(NodeModel | ConnectorModel)[]} drawingObject - provide the drawingObject value. * @private */ export function findObjectType(drawingObject) { var type; if (drawingObject) { if (drawingObject.type) { type = 'Connector'; } else if (drawingObject.shape && !drawingObject.type) { type = 'Node'; } } return type; } /** * setSwimLaneDefaults method \ * * @returns { void } setSwimLaneDefaults method .\ * @param {NodeModel | ConnectorModel} child - provide the child value. * @param {NodeModel | ConnectorModel} node - provide the node value. * @private */ export function setSwimLaneDefaults(child, node) { if (node instanceof Node) { if (!child.shape.header) { node.shape.hasHeader = false; } } } /** * getSpaceValue method \ * * @returns { number } getSpaceValue method .\ * @param {number[]} intervals - provide the intervals value. * @param {boolean} isLine - provide the isLine value. * @param {number} i - provide the i value. * @param {number} space - provide the space value. * @private */ export function getSpaceValue(intervals, isLine, i, space) { space = !isLine ? ((intervals[i - 1] !== undefined) ? intervals[i - 1] + space : 0) : space; return space; } /** * getInterval method \ * * @returns { number[] } getInterval method .\ * @param {number[]} intervals - provide the intervals value. * @param {boolean} isLine - provide the isLine value. * @private */ export function getInterval(intervals, isLine) { var newInterval = []; if (!isLine) { for (var k = 0; k < intervals.length; k++) { newInterval.push(intervals[parseInt(k.toString(), 10)]); } newInterval.push(intervals[newInterval.length - 2]); newInterval.push(intervals[newInterval.length - 2]); } else { newInterval = intervals; } return newInterval; } /** * setPortsEdges method \ * * @returns { Node } setPortsEdges method .\ * @param {Node} node - provide the node value. * @private */ export function setPortsEdges(node) { for (var k = 0; k < node.ports.length; k++) { node.ports[parseInt(k.toString(), 10)].inEdges = []; node.ports[parseInt(k.toString(), 10)].outEdges = []; } return node; } /** * setUMLActivityDefaults method \ * * @returns { void } setUMLActivityDefaults method .\ * @param {NodeModel | ConnectorModel} child - provide the child value. * @param {NodeModel | ConnectorModel} node - provide the node value. * @private */ export function setUMLActivityDefaults(child, node) { if (node instanceof Node) { var shape = child.shape.shape; switch (shape) { case 'JoinNode': if (!child.width) { node.width = 20; } if (!child.height) { node.height = 90; } if (!child.style || !child.style.fill) { node.style.fill = 'black'; } break; case 'ForkNode': if (!child.width) { node.width = 90; } if (!child.height) { node.height = 20; } if (!child.style || !child.style.fill) { node.style.fill = 'black'; } break; case 'InitialNode': if (!child.style || !child.style.fill) { node.style.fill = 'black'; } break; case 'FinalNode': if (!child.style || !child.style.fill) { node.style.fill = 'black'; } break; } } else { var flow = child.shape.flow; switch (flow) { case 'Object': if (!child.style || !child.style.strokeDashArray) { node.style.strokeDashArray = '8 4'; } if (!child.style || !child.style.strokeWidth) { node.style.strokeWidth = 2; } if (!child.targetDecorator || !child.targetDecorator.shape) { node.targetDecorator.shape = 'OpenArrow'; } break; case 'Control': if (!child.style || !child.style.strokeWidth) { node.style.strokeWidth = 2; } if (!child.targetDecorator || !child.targetDecorator.shape) { node.targetDecorator.shape = 'OpenArrow'; } if (!child.sourceDecorator || !child.sourceDecorator.shape) { node.sourceDecorator.shape = 'None'; } break; } } } /* eslint-disable */ /** * setConnectorDefaults method \ * * @returns { void } setConnectorDefaults method .\ * @param {ConnectorModel} child - provide the child value. * @param {ConnectorModel} node - provide the node value. * @private */ export function setConnectorDefaults(child, node) { switch ((child.shape).type) { case 'Bpmn': var bpmnFlow = child.shape.flow; switch (bpmnFlow) { case 'Sequence': if ((((child.shape.sequence) === 'Normal' && child.type !== 'Bezier')) || ((child.shape.sequence) === 'Default') || ((child.shape.sequence) === 'Conditional')) { if (node.targetDecorator && node.targetDecorator.style) { node.targetDecorator.style.fill = (child.targetDecorator && child.targetDecorator.style && child.targetDecorator.style.fill) || 'black'; } if ((child.shape.sequence) === 'Conditional' && node.sourceDecorator) { if (node.sourceDecorator.style) { node.sourceDecorator.style.fill = (child.sourceDecorator && child.sourceDecorator.style && child.sourceDecorator.style.fill) || 'white'; } node.sourceDecorator.width = (child.sourceDecorator && child.sourceDecorator.width) || 20; node.sourceDecorator.height = (child.sourceDecorator && child.sourceDecorator.width) || 10; } } break; case 'Association': if (((child.shape.association) === 'Default') || ((child.shape.association) === 'Directional') || ((child.shape.association) === 'BiDirectional')) { if (node.targetDecorator && node.targetDecorator.style) { node.targetDecorator.style.fill = (child.targetDecorator && child.targetDecorator.style && child.targetDecorator.style.fill) || 'black'; } if ((child.shape.association) === 'BiDirectional') { if (node.sourceDecorator && node.sourceDecorator.style) { node.sourceDecorator.style.fill = (child.sourceDecorator && child.sourceDecorator.style && child.sourceDecorator.style.fill) || 'white'; node.sourceDecorator.width = (child.sourceDecorator && child.sourceDecorator.width) || 5; node.sourceDecorator.height = (child.sourceDecorator && child.sourceDecorator.height) || 10; } } } break; case 'Message': if (node.style && !node.style.strokeDashArray) { node.style.strokeDashArray = (child.style && child.style.strokeDashArray) || '4 4'; } break; } break; case 'UmlActivity': var flow = child.shape.flow; switch (flow) { case 'Exception': if (((child.shape.association) === 'Directional') || ((child.shape.association) === 'BiDirectional')) { node.style.strokeDashArray = (child.style && child.style.strokeDashArray) || '2 2'; } break; } break; case 'UmlClassifier': var hasRelation = false; if (child.shape.relationship === 'Association') { hasRelation = true; } else if (child.shape.relationship === 'Inheritance') { if (node.targetDecorator && node.targetDecorator.style) { node.targetDecorator.style.fill = (child.targetDecorator && child.targetDecorator.style && child.targetDecorator.style.fill) || 'white'; } if (node.style) { hasRelation = true; } } else if (child.shape.relationship === 'Composition') { if (node.sourceDecorator && node.sourceDecorator.style) { node.sourceDecorator.style.fill = (child.sourceDecorator && child.sourceDecorator.style && child.sourceDecorator.style.fill) || 'black'; } hasRelation = true; } else if (child.shape.relationship === 'Aggregation' || child.shape.relationship === undefined) { if (node.sourceDecorator && node.sourceDecorator.style) { node.sourceDecorator.style.fill = (child.sourceDecorator && child.sourceDecorator.style && child.sourceDecorator.style.fill) || 'white'; } hasRelation = true; } else if (child.shape.relationship === 'Dependency') { if (node.sourceDecorator && node.sourceDecorator.style) { node.sourceDecorator.style.fill = (child.sourceDecorator && child.sourceDecorator.style && child.sourceDecorator.style.fill) || 'white'; } hasRelation = true; node.style.strokeDashArray = '4 4'; } else if (child.shape.relationship === 'Realization') { if (node.targetDecorator && node.targetDecorator.style) { node.targetDecorator.style.fill = (child.targetDecorator && child.targetDecorator.style && child.targetDecorator.style.fill) || 'white'; } hasRelation = true; node.style.strokeDashArray = '4 4'; } if (hasRelation) { node.style.strokeWidth = (child.style && child.style.strokeWidth) || 2; } break; } } /* eslint-enable */ /** * findNearestPoint method \ * * @returns { PointModel } findNearestPoint method .\ * @param {PointModel} reference - provide the reference value. * @param {PointModel} start - provide the start value. * @param {PointModel} end - provide the end value. * @private */ export function findNearestPoint(reference, start, end) { var shortestPoint; var shortest = Point.findLength(start, reference); var shortest1 = Point.findLength(end, reference); if (shortest > shortest1) { shortestPoint = end; } else { shortestPoint = start; } var angleBWStAndEnd = Point.findAngle(start, end); var angleBWStAndRef = Point.findAngle(shortestPoint, reference); var r = Point.findLength(shortestPoint, reference); var vaAngle = angleBWStAndRef + ((angleBWStAndEnd - angleBWStAndRef) * 2); return { x: (shortestPoint.x + r * Math.cos(vaAngle * Math.PI / 180)), y: (shortestPoint.y + r * Math.sin(vaAngle * Math.PI / 180)) }; } /** * pointsForBezier method \ * * @returns { PointModel[] } pointsForBezier method .\ * @param {ConnectorModel} connector - provide the connector value. * @private */ function pointsForBezier(connector) { var points = []; if (connector.type === 'Bezier') { var k = 0; for (var i = 0; i < connector.segments.length; i++) { var tolerance = 1.5; var segment = connector.segments[parseInt(i.toString(), 10)]; //const pt: PointModel = { x: 0, y: 0 }; var point1 = !Point.isEmptyPoint(segment.point1) ? segment.point1 : segment.bezierPoint1; var point2 = !Point.isEmptyPoint(segment.point2) ? segment.point2 : segment.bezierPoint2; var max = Number((connector.distance(point1, segment.points[0]) + connector.distance(point2, point1) + connector.distance(segment.points[1], point2)) / tolerance); for (var j = 0; j < max - 1; j = j + 10) { points[parseInt(k.toString(), 10)] = bezierPoints(connector, segment.points[0], !Point.isEmptyPoint(segment.point1) ? segment.point1 : segment.bezierPoint1, !Point.isEmptyPoint(segment.point2) ? segment.point2 : segment.bezierPoint2, segment.points[1], j, max); k++; } } } return points; } /** * isDiagramChild method \ * * @returns { boolean } isDiagramChild method .\ * @param {HTMLElement} htmlLayer - provide the htmlLayer value. * @private */ export function isDiagramChild(htmlLayer) { var element = htmlLayer.parentElement; do { if (hasClass(element, 'e-diagram')) { return true; } element = element.parentElement; } while (element); return false; } /** * groupHasType method \ * * @returns { boolean } groupHasType method .\ * @param {NodeModel} node - provide the node value. * @param {Shapes} type - provide the type value. * @param {{}} nameTable - provide the nameTable value. * @private */ // eslint-disable-next-line @typescript-eslint/ban-types export function groupHasType(node, type, nameTable) { var contains = false; if (node && node.children && node.children.length > 0) { var child = void 0; var i = 0; for (; i < node.children.length; i++) { child = nameTable[node.children[parseInt(i.toString(), 10)]]; if (child.shape.type === type) { return true; } return groupHasType(child, type, nameTable); } } return contains; } /** * groupHasType method \ * * @returns { void } groupHasType method .\ * @param {NodeModel | ConnectorModel} actualNode - provide the actualNode value. * @param { NodeModel | ConnectorModel} plainValue - provide the plainValue value. * @param {object} defaultValue - provide the defaultValue value. * @param {NodeModel | ConnectorModel} property - provide the property value. * @param {string} oldKey - provide the oldKey value. * @private */ export function updateDefaultValues(actualNode, plainValue, // eslint-disable-next-line @typescript-eslint/ban-types defaultValue, property, oldKey) { if (defaultValue && ((actualNode instanceof Connector) || actualNode && ((actualNode.shape && actualNode.shape.type !== 'SwimLane') || actualNode.shape === undefined))) { // eslint-disable-next-line @typescript-eslint/ban-types var keyObj = void 0; for (var _i = 0, _a = Object.keys(defaultValue); _i < _a.length; _i++) { var key = _a[_i]; keyObj = defaultValue["" + key]; if (key === 'shape' && keyObj.type) { actualNode.shape = { type: keyObj.type }; } if (keyObj) { if (Array.isArray(keyObj) && keyObj.length && keyObj.length > 0 && (oldKey !== 'annotations' && oldKey !== 'ports')) { if (actualNode["" + key].length > 0) { for (var i = 0; i <= actualNode["" + key].length; i++) { updateDefaultValues(actualNode["" + key], plainValue ? plainValue["" + key] : undefined, defaultValue["" + key], (key === 'annotations' || key === 'ports') ? actualNode : undefined, key); } } else { updateDefaultValues(actualNode["" + key], plainValue ? plainValue["" + key] : undefined, defaultValue["" + key], (key === 'annotations' || key === 'ports') ? actualNode : undefined, key); } } else if (keyObj instanceof Object && plainValue && (oldKey !== 'annotations' && oldKey !== 'ports')) { updateDefaultValues(actualNode["" + key], plainValue["" + key], defaultValue["" + key]); } else if ((oldKey !== 'annotations' && oldKey !== 'ports') && (plainValue && !plainValue["" + key]) || (!plainValue && actualNode && (actualNode["" + key] || actualNode["" + key] !== undefined))) { actualNode["" + key] = defaultValue["" + key]; } else { var createObject = void 0; if (oldKey === 'annotations' || oldKey === 'ports') { if (oldKey === 'annotations') { if (actualNode["" + key]) { updateDefaultValues(actualNode["" + key], plainValue["" + key], defaultValue["" + key]); } if (!actualNode["" + key]) { if (getObjectType(property) === Connector) { createObject = new PathAnnotation(property, 'annotations', defaultValue["" + key]); property.annotations.push(createObject); } else { createObject = new ShapeAnnotation(property, 'annotations', defaultValue["" + key]); property.annotations.push(createObject); } } } else { if (actualNode["" + key]) { updateDefaultValues(actualNode["" + key], plainValue["" + key], defaultValue["" + key]); } else { createObject = new PointPort(property, 'ports', defaultValue["" + key]); property.ports.push(createObject); } } } } } } } } /* tslint:disable:no-string-literal */ /** * updateLayoutValue method \ * * @returns { void } updateLayoutValue method .\ * @param {TreeInfo} actualNode - provide the actualNode value. * @param { object} defaultValue - provide the defaultValue value. * @param {INode[]} nodes - provide the nodes value. * @param {INode} node - provide the node value. * @private */ // eslint-disable-next-line @typescript-eslint/ban-types export function updateLayoutValue(actualNode, defaultValue, nodes, node) { // eslint-disable-next-line @typescript-eslint/ban-types var keyObj; var assistantKey = 'Role'; if (defaultValue) { for (var _i = 0, _a = Object.keys(defaultValue); _i < _a.length; _i++) { var key = _a[_i]; keyObj = defaultValue["" + key]; if (key === 'getAssistantDetails') { //Removed isBlazor code if (node.data["" + assistantKey] === defaultValue["" + key]['root']) { var assitants = defaultValue["" + key]['assistants']; for (var i = 0; i < assitants.length; i++) { for (var j = 0; j < nodes.length; j++) { if (nodes[parseInt(j.toString(), 10)].data["" + assistantKey] === assitants[parseInt(i.toString(), 10)]) { actualNode.assistants.push(nodes[parseInt(j.toString(), 10)].id); actualNode.children.splice(0, 1); } } } } } else if (keyObj) { actualNode["" + key] = defaultValue["" + key]; } } } if (!actualNode.hasSubTree && defaultValue.canEnableSubTree) { actualNode.orientation = node.layoutInfo.orientation; actualNode.type = node.layoutInfo.type; if (node.layoutInfo.offset !== actualNode.offset && (node.layoutInfo.offset) !== undefined) { actualNode.offset = node.layoutInfo.offset; } } node.layoutInfo.hasSubTree = actualNode.hasSubTree; } /* tslint:enable:no-string-literal */ /** * isPointOverConnector method \ * * @returns { boolean } isPointOverConnector method .\ * @param {ConnectorModel} connector - provide the connector value. * @param { PointModel} reference - provide the reference value. * @private */ export function isPointOverConnector(connector, reference) { //let intermediatePoints: PointModel[]; var intermediatePoints = connector.type === 'Bezier' ? pointsForBezier(connector) : connector.intermediatePoints; for (var i = 0; i < intermediatePoints.length - 1; i++) { var start = intermediatePoints[parseInt(i.toString(), 10)]; var end = intermediatePoints[i + 1]; var rect = Rect.toBounds([start, end]); rect.Inflate(connector.hitPadding); if (rect.containsPoint(reference)) { var intersectinPt = findNearestPoint(reference, start, end); var segment1 = { x1: start.x, x2: end.x, y1: start.y, y2: end.y }; var segment2 = { x1: reference.x, x2: intersectinPt.x, y1: reference.y, y2: intersectinPt.y }; var intersectDetails = intersect3(segment1, segment2); if (intersectDetails.enabled) { var distance = Point.findLength(reference, intersectDetails.intersectPt); if (Math.abs(distance) < connector.hitPadding) { return true; } } else { var rect_1 = Rect.toBounds([reference, reference]); rect_1.Inflate(3); if (rect_1.containsPoint(start) || rect_1.containsPoint(end)) { return true; } } if (Point.equals(reference, intersectinPt)) { return true; } } } if (connector.annotations.length > 0 || connector.ports.length > 0) { var container = connector.wrapper.children; for (var i = 3; i < container.length; i++) { var textElement = container[parseInt(i.toString(), 10)]; if (textElement.bounds.containsPoint(reference)) { return true; } } } return false; } /** * intersect3 method \ * * @returns { Intersection } intersect3 method .\ * @param {ConnectorModel} lineUtil1 - provide the lineUtil1 value. * @param { PointModel} lineUtil2 - provide the lineUtil2 value. * @private */ export function intersect3(lineUtil1, lineUtil2) { var point = { x: 0, y: 0 }; var l1 = lineUtil1; var l2 = lineUtil2; var d = (l2.y2 - l2.y1) * (l1.x2 - l1.x1) - (l2.x2 - l2.x1) * (l1.y2 - l1.y1); var na = (l2.x2 - l2.x1) * (l1.y1 - l2.y1) - (l2.y2 - l2.y1) * (l1.x1 - l2.x1); var nb = (l1.x2 - l1.x1) * (l1.y1 - l2.y1) - (l1.y2 - l1.y1) * (l1.x1 - l2.x1); /*( EJ2-42102 - Connector segments not update properly ) by sivakumar sekar - condition added to avoid bridging for overlapping segments in the connectors and to validate whether the connector is intersecting over the other */ if (d === 0 || ((lineUtil1.x1 === lineUtil2.x1 || lineUtil1.y1 === lineUtil2.y1) && (lineUtil1.x2 === lineUtil2.x2 || lineUtil1.y2 === lineUtil2.y2) && ((na === 0 || nb === 0) && d > 0))) { return { enabled: false, intersectPt: point }; } var ua = na / d; var ub = nb / d; if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) { point.x = l1.x1 + (ua * (l1.x2 - l1.x1)); point.y = l1.y1 + (ua * (l1.y2 - l1.y1)); return { enabled: true, intersectPt: point }; } return { enabled: false, intersectPt: point }; } /** * intersect2 method \ * * @returns { PointModel } intersect2 method .\ * @param {PointModel} start1 - provide the start1 value. * @param { PointModel} end1 - provide the end1 value. * @param { PointModel} start2 - provide the start2 value. * @param { PointModel} end2 - provide the end2 value. * @private */ export function intersect2(start1, end1, start2, end2) { var point = { x: 0, y: 0 }; var lineUtil1 = getLineSegment(start1.x, start1.y, end1.x, end1.y); var lineUtil2 = getLineSegment(start2.x, start2.y, end2.x, end2.y); var line3 = intersect3(lineUtil1, lineUtil2); if (line3.enabled) { return line3.intersectPt; } else { return point; } } /** * getLineSegment method \ * * @returns { Segment } getLineSegment method .\ * @param {number} x1 - provide the x1 value. * @param { number} y1 - provide the y1 value. * @param { number} x2 - provide the x2 value. * @param { number} y2 - provide the y2 value. * @private */ export function getLineSegment(x1, y1, x2, y2) { return { 'x1': Number(x1) || 0, 'y1': Number(y1) || 0, 'x2': Number(x2) || 0, 'y2': Number(y2) || 0 }; } /** * getPoints method \ * * @returns { PointModel[] } getPoints method .\ * @param {number} element - provide the element value. * @param { number} corners - provide the corners value. * @param { number} padding - provide the padding value. * @private */ export function getPoints(element, corners, padding) { var line = []; padding = padding || 0; var left = { x: corners.topLeft.x - padding, y: corners.topLeft.y }; var right = { x: corners.topRight.x + padding, y: corners.topRight.y }; var top = { x: corners.bottomRight.x, y: corners.bottomRight.y - padding }; var bottom = { x: corners.bottomLeft.x, y: corners.bottomLeft.y + padding }; line.push(left); line.push(right); line.push(top); line.push(bottom); return line; } /** * getTooltipOffset method \ * * @returns { PointModel[] } getTooltipOffset method .\ * @param {number} diagram - provide the diagram value. * @param { number} mousePosition - provide the mousePosition value. * @param { NodeModel | ConnectorModel | PointPortModel} node - provide the node value. * @param { string} type - provide the type value. * @private */ export function getTooltipOffset(diagram, mousePosition, node, type) { //let offset: PointModel; var inheritTooltip = (node instanceof Node) ? (node.constraints & NodeConstraints.InheritTooltip) : (node instanceof Connector) ? (node.constraints & ConnectorConstraints.InheritTooltip) : (node.constraints & PortConstraints.InheritTooltip); var objectTooltip = (node instanceof Node) ? (node.constraints & NodeConstraints.Tooltip) : (node instanceof Connector) ? (node.constraints & ConnectorConstraints.Tooltip) : (node.constraints & PortConstraints.ToolTip); var isMouseBased = ((!inheritTooltip && objectTooltip ? node.tooltip.relativeMode : diagram.tooltip.relativeMode) === 'Mouse') ? true : false; if (type === 'Mouse') { isMouseBased = true; } else if (type === 'Object') { isMouseBased = false; } var offset = tooltipOffset(node, mousePosition, diagram, isMouseBased); var rulerSize = getRulerSize(diagram); return { x: offset.x + rulerSize.width, y: offset.y + rulerSize.height }; } /** * tooltipOffset method \ * * @returns { PointModel } tooltipOffset method .\ * @param {NodeModel | ConnectorModel | PointPortModel} node - provide the node value. * @param { PointModel} mousePosition - provide the mousePosition value. * @param { Diagram } diagram - provide the diagram value. * @param { boolean} isMouseBased - provide the isMouseBased value. * @private */ function tooltipOffset(node, mousePosition, diagram, isMouseBased) { var point = {}; //let scale: number = diagram.scroller.transform.scale; var element = document.getElementById(diagram.element.id); var bounds; //EJ2-62120-Tooltip support for ports if (node instanceof Node || node instanceof Connector) { bounds = node.wrapper.bounds; } else { var objects = diagram.findObjectsUnderMouse(mousePosition); var obj = diagram.findObjectUnderMouse(objects, 'Select', false); var portElement = diagram.findElementUnderMouse(obj, mousePosition, diagram); bounds = portElement.bounds; } var rect = element.getBoundingClientRect(); /* eslint-enable */ //let horizontalOffset: number = diagram.scroller.horizontalOffset; //let verticalOffset: number = diagram.scroller.verticalOffset; switch (diagram.tooltipObject.position) { case 'BottomCenter': point = offsetPoint(mousePosition, bounds.bottomCenter, diagram, isMouseBased, (rect.width / 2), rect.height); break; case 'BottomLeft': case 'LeftBottom': point = offsetPoint(mousePosition, bounds.bottomLeft, diagram, isMouseBased, 0, rect.height); break; case 'BottomRight': case 'RightBottom': point = offsetPoint(mousePosition, bounds.bottomRight, diagram, isMouseBased, rect.width, rect.height); break; case 'LeftCenter': point = offsetPoint(mousePosition, bounds.middleLeft, diagram, isMouseBased, 0, (rect.height / 2)); break; case 'LeftTop': case 'TopLeft': point = offsetPoint(mousePosition, bounds.topLeft, diagram, isMouseBased, 0, 0); break; case 'RightCenter': point = offsetPoint(mousePosition, bounds.middleRight, diagram, isMouseBased, rect.width, (rect.height / 2)); break; case 'RightTop': case 'TopRight': point = offsetPoint(mousePosition, bounds.topRight, diagram, isMouseBased, rect.width, 0); break; case 'TopCenter': point = offsetPoint(mousePosition, bounds.topCenter, diagram, isMouseBased, (rect.width / 2), 0); break; } return point; } /** * offsetPoint method \ * * @returns { PointModel } offsetPoint method .\ * @param { PointModel} mousePosition - provide the mousePosition value. * @param { PointModel } bound - provide the diagram value. * @param { Diagram} diagram - provide the isMouseBased value. * @param { boolean} isMouseBased - provide the isMouseBased value. * @param { number} x - provide the isMouseBased value. * @param { number} y - provide the isMouseBased value. * @private */ function offsetPoint(mousePosition, bound, diagram, isMouseBased, x, y) { var point = {}; var scale = diagram.scroller.transform.scale; var horizontalOffset = diagram.scroller.horizontalOffset; var verticalOffset = diagram.scroller.verticalOffset; horizontalOffset = diagram.modifyClientOffset(horizontalOffset, true); verticalOffset = diagram.modifyClientOffset(verticalOffset, true); point.x = (isMouseBased ? mousePosition.x : bound.x) * scale + horizontalOffset - x; point.y = (isMouseBased ? mousePosition.y : bound.y) * scale + verticalOffset - y; return point; } /** * Gets the fixed user handles symbol \ * * @returns { DiagramElement } Gets the fixed user handles symbol .\ * @param {ConnectorFixedUserHandleModel | NodeFixedUserHandleModel} options - provide the options value. * @param { Canvas} fixedUserHandleContainer - provide the fixedUserHandleContainer value. * @private */ export function initFixedUserHandlesSymbol(options, fixedUserHandleContainer) { //let fixedUserHandleContent: PathElement | DiagramNativeElement; var fixedUserHandleContent = new PathElement(); fixedUserHandleContent.data = options.pathData; fixedUserHandleContent.height = options.height > 10 ? options.height - (options.padding.bottom + options.padding.top) : options.height; fixedUserHandleContent.width = options.width > 10 ? options.width - (options.padding.left + options.padding.right) : options.width; //Bug 912616: Not able to hide the fixedUserHandle in diagram fixedUserHandleContent.visible = options.visibility; fixedUserHandleContent.id = fixedUserHandleContainer.id + '_shape'; fixedUserHandleContent.inversedAlignment = false; fixedUserHandleContent.horizontalAlignment = 'Center'; fixedUserHandleContent.verticalAlignment = 'Center'; fixedUserHandleContent.style = { fill: options.iconStrokeColor, strokeColor: options.iconStrokeColor, strokeWidth: options.iconStrokeWidth }; fixedUserHandleContent.setOffsetWithRespectToBounds(0.5, 0.5, 'Fraction'); fixedUserHandleContent.relativeMode = 'Object'; fixedUserHandleContent.description = fixedUserHandleContainer.description || ''; return fixedUserHandleContent; } /** * sort method \ * * @returns { (NodeModel | ConnectorModel)[] } sort method .\ * @param {(NodeModel | ConnectorModel)[]} objects - provide the options value. * @param { DistributeOptions} option - provide the fixedUserHandleContainer value. * @private */ export function sort(objects, option) { var i = 0; var j = 0; var temp; for (i = 0; i < objects.length; i++) { for (j = i + 1; j < objects.length; j++) { // 986178 - Distribution of Objects Updates Incorrectly var b = getBounds(objects[parseInt(i.toString(), 10)].wrapper); var bounds = getBounds(objects[parseInt(j.toString(), 10)].wrapper); if (option === 'Top' || option === 'Bottom' || option === 'BottomToTop' || option === 'Middle') { if (b.center.y > bounds.center.y) { temp = objects[parseInt(i.toString(), 10)]; objects[parseInt(i.toString(), 10)] = objects[parseInt(j.toString(), 10)]; objects[parseInt(j.toString(), 10)] = temp; } } else { if (b.center.x > bounds.center.x) { temp = objects[parseInt(i.toString(), 10)]; objects[parseInt(i.toString(), 10)] = objects[parseInt(j.toString(), 10)]; objects[parseInt(j.toString(), 10)] = temp; } } } } return objects; } /** * getAnnotationPosition method \ * * @returns {SegmentInfo } getAnnotationPosition method .\ * @param {PointModel[]} pts - provide the pts value. * @param { PathAnnotation | ConnectorFixedUserHandle} annotation - provide the annotation value. * @param { Rect } bound - provide the bound value. * @private */ // eslint-disable-next-line @typescript-eslint/no-unused-vars export function getAnnotationPosition(pts, annotation, bound) { //let angle: number; //let getloop: SegmentInfo; //let point: PointModel; var getloop = getOffsetOfConnector(pts, annotation); var angle = Point.findAngle(pts[getloop.index], pts[getloop.index + 1]); var alignednumber = getAlignedPosition(annotation); var point = Point.transform(getloop.point, angle + 45, alignednumber); getloop.point = point; getloop.angle = angle; return getloop; } /** * getPortsPosition method \ * * @returns {SegmentInfo } getPortsPosition method .\ * @param {PointModel[]} pts - provide the pts value. * @param { Port} ports - provide the ports value. * @param { Rect } bound - provide the bound value. * @private */ // eslint-disable-next-line @typescript-eslint/no-unused-vars export function getPortsPosition(pts, ports, bound) { var getloop = getOffsetOfPorts(pts, ports); var angle = Point.findAngle(pts[getloop.index], pts[getloop.index + 1]); var alignednumber = getAlignedPositionForPorts(ports); var point = Point.transform(getloop.point, angle + 45, alignednumber); getloop.point = point; getloop.angle = angle; return getloop; } /** * getOffsetOfPorts method \ * * @returns {SegmentInfo } getOffsetOfPorts method .\ * @param {PointModel[]} points - provide the pts value. * @param { PathAnnotation | ConnectorFixedUserHandle} ports - provide the ports value. * @private */ export function getOffsetOfPorts(points, ports) { // eslint-disable-next-line var distance = 0; var offset = ports.offset; var point; var angle; var pointDistance = []; var prevLength; var kCount; for (var j = 0; j < points.length - 1; j++) { distance += Point.distancePoints(points[parseInt(j.toString(), 10)], points[j + 1]); pointDistance.push(distance); } var offsetLength = offset * distance; for (var k = 0; k < pointDistance.length; k++) { if (pointDistance[parseInt(k.toString(), 10)] >= offsetLength) { angle = Point.findAngle(points[parseInt(k.toString(), 10)], points[k + 1]); point = Point.transform(points[parseInt(k.toString(), 10)], angle, offsetLength - (prevLength || 0)); kCount = k; return { point: point, index: kCount }; } prevLength = pointDistance[parseInt(k.toString(), 10)]; } return { point: point, index: kCount }; } /** * getAlignedPosition method . To get the port alignment position \ * * @returns {number } getAlignedPosition method .\ * @param {PointModel[]} ports - provide the annotation value. * @private */ export function getAlignedPositionForPorts(ports) { var constant = 0; var state = 0; switch (ports.alignment) { case 'Center': state = 0; break; case 'Before': state = -((0) / 2 + constant); break; case 'After': state = ((0) / 2 + constant); break; } return state; } /** * getOffsetOfConnector method \ * * @returns {SegmentInfo } getOffsetOfConnector method .\ * @param {PointModel[]} points - provide the pts value. * @param { PathAnnotation | ConnectorFixedUserHandle} annotation - provide the annotation value. * @private */ export function getOffsetOfConnector(points, annotation) { // eslint-disable-next-line var length = 0; var offset = annotation.offset; var point; var angle; var lengths = []; var prevLength; var kCount; for (var j = 0; j < points.length - 1; j++) { length += Point.distancePoints(points[parseInt(j.toString(), 10)], points[j + 1]); lengths.push(length); } var offsetLength = offset * length; for (var k = 0; k < lengths.length; k++) { if (lengths[parseInt(k.toString(), 10)] >= offsetLength) { angle = Point.findAngle(points[parseInt(k.toString(), 10)], points[k + 1]); point = Point.transform(points[parseInt(k.toString(), 10)], angle, offsetLength - (prevLength || 0)); kCount = k; return { point: point, index: kCount }; } prevLength = lengths[parseInt(k.toString(), 10)]; } return { point: point, index: kCount }; } /** * getAlignedPosition method \ * * @returns {number } getAlignedPosition method .\ * @param {PointModel[]} annotation - provide the annotation value. * @private */ export function getAlignedPosition(annotation) { var cnst; if ((annotation instanceof ConnectorFixedUserHandle)) { cnst = 0; } else { cnst = annotation.content === undefined ? 10 : 0; } var state = 0; switch (annotation.alignment) { case 'Center': state = 0; break; case 'Before': state = -((0) / 2 + cnst); break; case 'After': state = ((0) / 2 + cnst); break; } return state; } /** * alignLabelOnSegments method \ * * @returns {Alignment } alignLabelOnSegments method .\ * @param {PathAnnotation | ConnectorFixedUserHandle} obj - provide the obj value. * @param { number } ang - provide the ang value. * @param { PointModel[] } pts - provide the pts value. * @private */ export function alignLabelOnSegments(obj, ang, pts) { //let angle: number = ang % 360; ang %= 360; var fourty5 = 45; var one35 = 135; var two25 = 225; var three15 = 315; var vAlign; var hAlign; switch (obj.alignment) { case 'Before': if (ang >= fourty5 && ang <= one35) { hAlign = 'right'; vAlign = obj.offset === 0.5 ? 'center' : 'top'; } else if (ang >= two25 && ang <= three15) { hAlign = 'left'; vAlign = obj.offset === 0.5 ? 'center' : 'bottom'; } else if (ang > fourty5 && ang < two25) { vAlign = 'top'; hAlign = obj.offset === 0.5 ? 'center' : 'right'; } else { vAlign = 'bottom'; hAlign = (obj.offset === 0.5) ? 'center' : 'left'; } break; case 'After': if (ang >= fourty5 && ang <= one35) { hAlign = 'left'; vAlign = obj.offset === 0.5 ? 'center' : 'top'; } else if (ang >= two25 && ang <= three15) { hAlign = 'right'; vAlign = obj.offset === 0.5 ? 'center' : 'bottom'; } else if (ang > fourty5 && ang < two25) { vAlign = 'bottom'; hAlign = obj.offset === 0.5 ? 'center' : 'right'; } else { vAlign = 'top'; hAlign = obj.offset === 0.5 ? 'center' : 'left'; } break; case 'Center': hAlign = !isNullOrUndefined(obj.horizontalAlignment) ? obj.horizontalAlignment.toLowerCase() : 'center'; vAlign = !isNullOrUndefined(obj.verticalAlignment) ? obj.verticalAlignment.toLowerCase() : 'center'; break; } if (obj.offset === 0 || obj.offset === 1) { //let direction: string; var direction = getBezierDirection(pts[0], pts[1]); switch (direction) { case 'left': hAlign = obj.offset === 0 ? 'right' : 'left'; break; case 'right': hAlign = obj.offset === 0 ? 'left' : 'right'; break; case 'bottom': vAlign = obj.offset === 0 ? 'top' : 'bottom'; break; case 'top': vAlign = obj.offset === 0 ? 'bottom' : 'top'; break; } } return { hAlign: hAlign, vAlign: vAlign }; } /** * getBezierDirection method \ * * @returns {string } getBezierDirection method .\ * @param {PointModel} src - provide the src value. * @param { PointModel } tar - provide the tar value. * @private */ export function getBezierDirection(src, tar) { if (Math.abs(tar.x - src.x) > Math.abs(tar.y - src.y)) { return src.x < tar.x ? 'right' : 'left'; } else { return src.y < tar.y ? 'bottom' : 'top'; } } /** * removeChildNodes method \ * * @returns {void } removeChildNodes method .\ * @param {NodeModel} node - provide the node value. * @param { Diagram } diagram - provide the diagram value. * @private */ export function removeChildNodes(node, diagram) { if (node instanceof Node && node.children) { for (var i = 0; i < node.children.length; i++) { if (diagram.nameTable[node.children[parseInt(i.toString(), 10)]].children) { removeChildNodes(node, diagram); } diagram.removeFromAQuad(diagram.nameTable[node.children[parseInt(i.toString(), 10)]]); diagram.removeObjectsFromLayer(diagram.nameTable[node.children[parseInt(i.toString(), 10)]]); delete diagram.nameTable[node.children[parseInt(i.toString(), 10)]]; } } } /** * getChild method \ * * @returns {string[] } getChild method .\ * @param {Canvas} child - provide the child value. * @param { string[] } children - provide the children value. * @private */ export function getChild(child, children) { if (child && child.children && child.children.length > 0) { for (var j = 0; j < child.children.length; j++) { var subChild = child.children[parseInt(j.toString(), 10)]; if (subChild instanceof Canvas) { getChild(subChild, children); } } } if (children.indexOf(child.id) === -1) { children.push(child.id); } return children; } /** * getSwimLaneChildren method \ * * @returns {string[] } getSwimLaneChildren method .\ * @param {NodeModel[]} nodes - provide the nodes value. * @private */ export function getSwimLaneChildren(nodes) { var children = []; var node; var grid; var childTable; var child; var gridChild = 'childTable'; for (var i = 0; i < nodes.length; i++) { node = nodes[parseInt(i.toString(), 10)]; if (node.shape.type === 'SwimLane') { grid = node.wrapper.children[0]; childTable = grid["" + gridChild]; for (var _i = 0, _a = Object.keys(childTable); _i < _a.length; _i++) { var key = _a[_i]; child = childTable["" + key]; children = getChild(child, children); } } } return children; } /** * removeUnnecessaryNodes method \ * * @returns {void } removeUnnecessaryNodes method .\ * @p