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.

908 lines (907 loc) 200 kB
/* eslint-disable jsdoc/require-returns */ /* eslint-disable jsdoc/require-param */ /* eslint-disable valid-jsdoc */ /* eslint-disable no-case-declarations */ import { Browser } from '@syncfusion/ej2-base'; import { Point } from '../primitives/point'; import { GroupableView } from '../core/containers/container'; import { Connector } from '../objects/connector'; import { NodeDrawingTool, ConnectorDrawingTool, TextDrawingTool, FreeHandTool } from './tool'; import { PolygonDrawingTool, PolyLineDrawingTool, FixedUserHandleTool } from './tool'; import { Native, Node, Lane, Phase, UmlClassAttribute, UmlClassMethod, UmlEnumerationMember } from '../objects/node'; import { SelectTool, MoveTool, ResizeTool, RotateTool, ConnectTool, ExpandTool, LabelTool, ZoomPanTool } from './tool'; import { LabelDragTool, LabelResizeTool, LabelRotateTool } from './tool'; import { ConnectorEditing } from './connector-editing'; import { Selector } from '../objects/node'; import { findToolToActivate, isSelected, getCursor, contains, findPortToolToActivate } from './actions'; import { DiagramAction, KeyModifiers, Keys, DiagramEvent, DiagramTools, RendererAction, DiagramConstraints, PortConstraints } from '../enum/enum'; import { BlazorAction, ScrollActions } from '../enum/enum'; import { isPointOverConnector, findObjectType, insertObject, getObjectFromCollection, getTooltipOffset, findParentInSwimlane, findPort, sortNodeCollection } from '../utility/diagram-util'; import { getObjectType, getInOutConnectPorts, removeChildNodes, cloneBlazorObject, checkPort } from '../utility/diagram-util'; import { canZoomPan, canDraw, canDrag, canZoomTextEdit, canVitualize, canPreventClearSelection, canSingleSelect, canMultiSelect } from './../utility/constraints-util'; import { selectionHasConnector } from '../utility/diagram-util'; import { canMove, canEnablePointerEvents, canSelect, canEnableToolTip } from './../utility/constraints-util'; import { canOutConnect, canInConnect, canPortInConnect, canPortOutConnect, canAllowDrop, canUserInteract, defaultTool } from './../utility/constraints-util'; import { updateTooltip } from '../objects/tooltip'; import { PortVisibility, NodeConstraints, ConnectorConstraints, AnnotationConstraints, RealAction } from '../enum/enum'; import { addTouchPointer, measureHtmlText, getAdornerLayerSvg } from '../utility/dom-util'; import { TextElement } from '../core/elements/text-element'; import { Size } from '../primitives/size'; import { cloneObject as clone, cloneObject } from './../utility/base-util'; import { Rect } from '../primitives/rect'; import { identityMatrix, rotateMatrix, transformPointByMatrix } from './../primitives/matrix'; import { removeRulerMarkers, drawRulerMarkers, getRulerSize, updateRuler } from '../ruler/ruler'; import { canContinuousDraw, canDrawOnce } from '../utility/constraints-util'; import { getFunction, cornersPointsBeforeRotation } from '../utility/base-util'; import { ShapeAnnotation, PathAnnotation } from '../objects/annotation'; import { updateCanvasBounds, checkChildNodeInContainer, checkParentAsContainer, removeChildInContainer } from './container-interaction'; import { moveChildInStack, renderStackHighlighter } from './container-interaction'; import { updateSwimLaneObject } from '../utility/swim-lane-util'; import { getConnectors, updateHeaderMaxWidth, laneInterChanged, updateConnectorsProperties } from '../utility/swim-lane-util'; import { DiagramHtmlElement } from '../core/elements/html-element'; import { containsBounds, getFlippedPoint, getPoint, isLabelFlipped, PathElement, randomId } from '../index'; import { isBlazor } from '@syncfusion/ej2-base'; import { PathPort, PointPort } from '../objects/port'; import { Overview } from '../../overview/overview'; import { ConnectorFixedUserHandle, NodeFixedUserHandle } from '../objects/fixed-user-handle'; import { UserHandle } from '../interaction/selector'; /** * This module handles the mouse and touch events */ var DiagramEventHandler = /** @class */ (function () { /** @private */ function DiagramEventHandler(diagram, commandHandler) { this.currentAction = 'None'; this.previousAction = 'None'; this.previousTarget = null; /** @private */ this.touchArgs = undefined; /** @private */ this.focus = false; this.isBlocked = false; this.isMouseDown = false; this.inAction = false; this.doingAutoScroll = false; this.diagram = null; this.objectFinder = null; this.tool = null; this.eventArgs = null; this.previousElement = null; this.isKeyUp = true; this.keyCount = 0; this.isNudgeKey = false; this.commandObj = {}; this.keyArgs = {}; this.diagram = diagram; this.objectFinder = new ObjectFinder(); this.commandHandler = commandHandler; } Object.defineProperty(DiagramEventHandler.prototype, "action", { get: function () { return this.currentAction; }, set: function (action) { if (action !== this.currentAction) { if (this.currentAction === 'PortDraw') { this.diagram.tool &= ~DiagramTools.DrawOnce; //EJ2-70550 - Connector disconnected from source and target while dragging mutliple selected element if (this.diagram.currentDrawingObject) { this.diagram.currentDrawingObject = null; } if (this.tool) { //973919: Runtime Error When Deleting Connector in elementDraw Event if (this.tool.drawingObject) { this.tool.drawingObject = null; } this.tool.mouseUp({ position: this.currentPosition }); } this.tool = null; } if (action === 'Rotate' || action === 'LabelRotate') { this.diagram.diagramCanvas.classList.add('e-diagram-rotate'); } else if (this.currentAction === 'Rotate' || this.currentAction === 'LabelRotate') { this.diagram.diagramCanvas.classList.remove('e-diagram-rotate'); } this.currentAction = action; //Ej2-26204 - Exception occurs when remove method called without mouse Interaction if (this.currentAction !== 'None' && this.currentAction !== 'Select' && !(this.diagram.diagramActions & DiagramAction.TextEdit) && !(this.currentPosition && this.commandHandler.isUserHandle(this.currentPosition)) && !(this.currentAction === 'FixedUserHandle')) { this.diagram.diagramActions = this.diagram.diagramActions | DiagramAction.ToolAction; } else { this.diagram.diagramActions = this.diagram.diagramActions & ~DiagramAction.ToolAction; } this.diagram.setCursor(this.diagram.getCursor(action, this.inAction)); } }, enumerable: true, configurable: true }); Object.defineProperty(DiagramEventHandler.prototype, "blocked", { get: function () { return this.isBlocked; }, set: function (blocked) { this.isBlocked = blocked; if (this.blocked) { this.diagram.setCursor('not-allowed'); } else { this.diagram.setCursor(this.diagram.getCursor(this.action, this.inAction)); } }, enumerable: true, configurable: true }); /** @private */ DiagramEventHandler.prototype.getMousePosition = function (e) { var touchArg; var offsetX; var offsetY; if (e.type.indexOf('touch') !== -1) { touchArg = e; offsetX = touchArg.changedTouches[0].clientX; offsetY = touchArg.changedTouches[0].clientY; } else { offsetX = e.clientX; offsetY = e.clientY; } offsetX = this.diagram.modifyClientOffset(offsetX); offsetY = this.diagram.modifyClientOffset(offsetY); var position = new Size(); position = getRulerSize(this.diagram); var boundingRect = this.diagram.element.getBoundingClientRect(); this.diagram.modifyBounds(boundingRect); offsetX = offsetX + this.diagram.diagramCanvas.scrollLeft - boundingRect.left - position.width; offsetY = offsetY + this.diagram.diagramCanvas.scrollTop - boundingRect.top - position.height; offsetX /= this.diagram.scroller.transform.scale; offsetY /= this.diagram.scroller.transform.scale; offsetX -= this.diagram.scroller.transform.tx; offsetY -= this.diagram.scroller.transform.ty; return { x: offsetX, y: offsetY }; }; /** * @private */ DiagramEventHandler.prototype.windowResize = function (evt) { var _this = this; if (this.resizeTo) { clearTimeout(this.resizeTo); } this.resizeTo = setTimeout(function () { _this.updateViewPortSize(_this.diagram.element); }, 300); return false; }; /** * @private */ DiagramEventHandler.prototype.updateViewPortSize = function (element) { var container = document.getElementById(element.id); if (container) { var bounds = container.getBoundingClientRect(); this.diagram.modifyBounds(bounds); this.diagram.scroller.setViewPortSize(bounds.width, bounds.height); var position = new Size(); position = getRulerSize(this.diagram); var width = this.diagram.getSizeValue(this.diagram.width, position.width); var height = this.diagram.getSizeValue(this.diagram.height, position.height); this.diagram.diagramCanvas.style.width = width; this.diagram.diagramCanvas.style.height = height; this.diagram.scroller.setSize(); this.diagram.transformLayers(); if (this.diagram.rulerSettings.showRulers) { updateRuler(this.diagram); } if (this.diagram.views.length > 1) { //884316 - updating overview after window resize for (var _i = 0, _a = this.diagram.views; _i < _a.length; _i++) { var temp = _a[_i]; var view = this.diagram.views["" + temp]; if ((view instanceof Overview)) { //Calling onproperty change method to update overview. view.onPropertyChanged({ sourceID: view.sourceID }, {}); } } } } }; /** @private */ DiagramEventHandler.prototype.canHideResizers = function () { return ((this.tool instanceof MoveTool || this.tool instanceof RotateTool) && this.isMouseDown); }; /** @private */ DiagramEventHandler.prototype.updateCursor = function () { if ((this.diagram.selectedItems.nodes.length === 1 || this.diagram.selectedItems.connectors.length === 1)) { var list = []; list = list.concat(this.diagram.selectedItems.nodes, this.diagram.selectedItems.connectors); // Bug fix - EJ2-44495 -Node does not gets selected on slight movement of mouse when drag constraints disabled for node this.blocked = (this.eventArgs && this.eventArgs.source && !canMove(this.eventArgs.source)) ? false : (this.isMouseDown && list.length === 1 && this.tool instanceof SelectTool && !canMove(list[0])); } }; DiagramEventHandler.prototype.isForeignObject = function (target, isTextBox) { var foreignobject = target; // 976504 - Annotation Template Drag is improper in diagram while template is image source if (foreignobject && foreignobject.tagName.toLowerCase() !== 'img') { while (foreignobject.parentNode !== null) { if (typeof foreignobject.className === 'string' && ((!isTextBox && foreignobject.className.indexOf('foreign-object') !== -1) || (isTextBox && foreignobject.className.indexOf('e-diagram-text-edit') !== -1))) { return foreignobject; } else { foreignobject = foreignobject.parentNode; } } } return null; }; DiagramEventHandler.prototype.isMetaKey = function (evt) { //EJ2-55887 - added the beow code to perform pinch zoom in mac os and windows while pinch zoom all browser return ctrl key as true. if (evt.type === 'mousewheel') { return evt.ctrlKey; } else { return navigator.platform.match('Mac') ? evt.metaKey : evt.ctrlKey; } }; DiagramEventHandler.prototype.renderUmlHighLighter = function (args) { this.diagram.commandHandler.removeStackHighlighter(); var node = this.diagram.selectedItems.nodes[0]; if (node && node.container && node.container.type === 'Stack' && node.shape.type === 'UmlClassifier') { var bound = node.wrapper.bounds; if (!bound.containsPoint(this.currentPosition)) { // eslint-disable-next-line max-len var objects = this.diagram.findObjectsUnderMouse({ x: this.currentPosition.x - 20, y: this.currentPosition.y }); var target = this.diagram.findObjectUnderMouse(objects, this.action, this.inAction); if (target && target.parentId && (target.parentId === node.id)) { // eslint-disable-next-line max-len var isVertical = this.diagram.nameTable[target.parentId].container.orientation === 'Vertical'; renderStackHighlighter(target.wrapper, isVertical, args.position, this.diagram, true); } } } }; DiagramEventHandler.prototype.isDeleteKey = function (key, value) { return (navigator.platform.match('Mac') && key === 'Backspace' && value === 'delete'); }; DiagramEventHandler.prototype.isMouseOnScrollBar = function (evt) { var x = evt.offsetX; var y = evt.offsetY; var diagramCanvas = this.diagram.diagramCanvas; var height = diagramCanvas.offsetHeight; var width = diagramCanvas.offsetWidth; var topLeft; var topRight; var bottomLeft; var bottomRight; var bounds; if (height < diagramCanvas.scrollHeight) { //default scrollbar width in browser is '17pixels'. topLeft = { x: (width - 17), y: 0 }; topRight = { x: width, y: 0 }; bottomLeft = { x: (width - 17), y: height }; bottomRight = { x: width, y: height }; bounds = Rect.toBounds([topLeft, topRight, bottomLeft, bottomRight]); // EJ2-64563-Added below code to calculate the bounds x and y value if vertical offset != 0 if (this.diagram.scroller.verticalOffset !== 0) { bounds.x = bounds.x - this.diagram.scroller.horizontalOffset; bounds.y = bounds.y - this.diagram.scroller.verticalOffset; } if (bounds.containsPoint({ x: x, y: y })) { return true; } } if (width < diagramCanvas.scrollWidth) { topLeft = { x: 0, y: (height - 17) }; topRight = { x: width, y: (height - 17) }; bottomLeft = { x: 0, y: height }; bottomRight = { x: width, y: height }; bounds = Rect.toBounds([topLeft, topRight, bottomLeft, bottomRight]); // EJ2-64563-Added below code to calculate the bounds x and y value if horizontal offset != 0 if (this.diagram.scroller.horizontalOffset !== 0) { bounds.x = bounds.x - this.diagram.scroller.horizontalOffset; bounds.y = bounds.y - this.diagram.scroller.verticalOffset; } if (bounds.containsPoint({ x: x, y: y })) { return true; } } return false; }; /** @private */ DiagramEventHandler.prototype.updateVirtualization = function () { var _this = this; var delay = 50; //let removeObjectInterval: Object; var removeObjectInterval = setInterval(function (args) { _this.diagram.removeVirtualObjects(removeObjectInterval); }, delay); setTimeout(function () { _this.diagram.deleteVirtualObject = true; }, delay); }; DiagramEventHandler.prototype.checkPreviousAction = function () { if (this.action !== this.previousAction && this.diagram.selectedItems.userHandles.length) { for (var i = 0; i < this.diagram.selectedItems.userHandles.length; i++) { if (this.previousAction && this.diagram.selectedItems.userHandles[parseInt(i.toString(), 10)]) { this.checkUserHandleEvent(DiagramEvent.onUserHandleMouseLeave); this.previousAction = 'None'; } } } if (this.action !== this.previousAction) { // If the mouse leaves the fixed user handle, the tooltip is closed this.checkFixedUserHandleEvent(DiagramEvent.onFixedUserHandleMouseLeave, this.targetItem, this.previousTarget); this.previousTarget = null; this.targetItem = null; } }; DiagramEventHandler.prototype.checkUserHandleEvent = function (eventName) { if (this.diagram.selectedItems && this.diagram.selectedItems.userHandles.length > 0) { var currentAction = (eventName === DiagramEvent.onUserHandleMouseLeave) ? this.previousAction : this.action; var arg = { element: undefined }; for (var i = 0; i < this.diagram.selectedItems.userHandles.length; i++) { if ((currentAction === this.diagram.selectedItems.userHandles[parseInt(i.toString(), 10)].name) || (eventName === DiagramEvent.onUserHandleMouseUp && currentAction === 'Select')) { arg.element = this.diagram.selectedItems.userHandles[parseInt(i.toString(), 10)]; this.userHandle(eventName, i, arg, null); } } } }; DiagramEventHandler.prototype.userHandle = function (eventName, i, arg, targetItem) { if (eventName === DiagramEvent.onUserHandleMouseEnter || eventName === DiagramEvent.onFixedUserHandleMouseEnter) { this.previousAction = this.action; // EJ2-32213- Added the below code to check whether the userhandle has tooltip content. // If userhandle has tooltip content then we open the tooltip based on the userhandle shape if (arg.element.tooltip && arg.element.tooltip.openOn === 'Auto' && arg.element.tooltip.content !== '') { updateTooltip(this.diagram, arg.element); var targetEle = void 0; if (arg.element.pathData) { if (eventName === DiagramEvent.onUserHandleMouseEnter) { targetEle = document.getElementById(this.diagram.selectedItems.userHandles[parseInt(i.toString(), 10)].name + '_userhandle'); } else if (eventName === DiagramEvent.onFixedUserHandleMouseEnter) { targetEle = document.getElementById(targetItem.id + '_' + targetItem.fixedUserHandles[parseInt(i.toString(), 10)].id + '_groupElement'); } } else if (arg.element.source) { targetEle = document.getElementById(this.diagram.selectedItems.userHandles[parseInt(i.toString(), 10)].name + '_image'); } else if (arg.element.content) { targetEle = document.getElementById(this.diagram.selectedItems.userHandles[parseInt(i.toString(), 10)].name + '_shape_native_element'); } else { targetEle = document.getElementById(this.diagram.selectedItems.userHandles[parseInt(i.toString(), 10)].name + '_shape_html_element'); } //892828: Flickering of tooltip while hovering userhandle if (arg.element.tooltip.openOn === 'Auto' && (arg.element !== this.isUserHandleHover)) { this.isUserHandleHover = arg.element; this.diagram.tooltipObject.open(targetEle); } } this.diagram.triggerEvent(eventName, arg); } if (eventName === DiagramEvent.onUserHandleMouseDown) { this.userHandleObject = this.diagram.selectedItems.userHandles[parseInt(i.toString(), 10)].name; this.diagram.triggerEvent(eventName, arg); } else if (eventName === DiagramEvent.onFixedUserHandleMouseDown) { this.diagram.triggerEvent(eventName, arg); } if (eventName === DiagramEvent.onUserHandleMouseUp) { // //EJ2-982152 -onUserHandleMouseUp event not triggers on user handles with content, image or a template var element = void 0; if (arg.element.pathData) { element = document.getElementById(this.diagram.selectedItems.userHandles[parseInt(i.toString(), 10)].name + '_userhandle'); } else if (arg.element.source) { element = document.getElementById(this.diagram.selectedItems.userHandles[parseInt(i.toString(), 10)].name + '_image'); } else if (arg.element.content) { element = document.getElementById(this.diagram.selectedItems.userHandles[parseInt(i.toString(), 10)].name + '_shape_native_element'); } else { element = document.getElementById(this.diagram.selectedItems.userHandles[parseInt(i.toString(), 10)].name + '_shape_html_element'); } if (this.commandHandler.isUserHandle(this.currentPosition) && element && element.id && (element.id.split(this.userHandleObject).length > 1)) { //EJ2-838423 -onUserHandleMouseUp event triggers multiple times this.diagram.triggerEvent(eventName, arg); } } else if (eventName === DiagramEvent.onFixedUserHandleMouseUp) { this.diagram.triggerEvent(eventName, arg); } if (eventName === DiagramEvent.onUserHandleMouseLeave || eventName === DiagramEvent.onFixedUserHandleMouseLeave) { if (this.diagram.tooltipObject && this.diagram.tooltipObject.openOn !== 'Custom') { this.isUserHandleHover = null; this.diagram.tooltipObject.close(); } this.diagram.triggerEvent(eventName, arg); } }; // In the method, verify the fixed user handle and manage the opening and closing of the tooltip according to mouse events DiagramEventHandler.prototype.checkFixedUserHandleEvent = function (eventName, targetItem, wrapper) { if (targetItem && targetItem.fixedUserHandles.length > 0) { var arg = { element: undefined }; var userid = void 0; var currentAction = void 0; for (var i = 0; i < targetItem.fixedUserHandles.length; i++) { userid = targetItem.fixedUserHandles[parseInt(i.toString(), 10)].id; if (wrapper && wrapper.id && (wrapper.id.indexOf(userid) > -1)) { currentAction = userid; this.previousTarget = wrapper; this.targetItem = targetItem; } if (currentAction === targetItem.fixedUserHandles[parseInt(i.toString(), 10)].id) { arg.element = targetItem.fixedUserHandles[parseInt(i.toString(), 10)]; this.userHandle(eventName, i, arg, targetItem); } } } }; DiagramEventHandler.prototype.mouseDown = function (evt) { var _this = this; //Bug 913785: Tooltip for node is not showing when touch and hold on node in mobile device. if (evt.type === 'touchstart') { this.timeOutTapHold = setTimeout(function () { if (_this.action === 'Select' || _this.action === 'Drag') { _this.mouseMove(evt, undefined); } }, 500); } // EJ2-57541 - Added the below code to check whether diagram tool is instance of node drawing tool or connector drawing tool. // If node or connector drawing tool means then we have returned without perform any operation. // 923532: Event 'Completed' state not triggered with multiple button click Action if ((this.inAction === true && ((this.tool) instanceof NodeDrawingTool || this.tool instanceof ConnectorDrawingTool)) || (this.inAction === true && (evt.buttons && (evt.buttons & (evt.buttons - 1)) !== 0))) { return; } this.focus = true; //let touches: TouchList; var touches = evt.touches; var isSymblDragging = document.getElementsByClassName('e-dragclone')[0] ? true : false; if (this.isMouseOnScrollBar(evt) && !isSymblDragging) { this.isScrolling = true; evt.preventDefault(); return; } // commanded by gowtham- unwanted cloning of selectedItems // if (isBlazor()) { // this.commandHandler.oldSelectedObjects = cloneObject(this.diagram.selectedItems); // } this.checkFixedUserHandleEvent(DiagramEvent.onFixedUserHandleMouseDown, this.targetItem, this.previousTarget); this.checkUserHandleEvent(DiagramEvent.onUserHandleMouseDown); if (!this.checkEditBoxAsTarget(evt) && (canUserInteract(this.diagram)) || (canZoomPan(this.diagram) && !defaultTool(this.diagram))) { this.diagram.restrictedDeltaValue = { x: 0, y: 0 }; if (this.action === 'Select' || this.action === 'Drag') { this.diagram.updatePortVisibility(this.hoverElement, PortVisibility.Hover, true); } if (((this.tool instanceof PolygonDrawingTool || this.tool instanceof PolyLineDrawingTool) && (evt.button === 2 || evt.buttons === 2))) { // eslint-disable-next-line var arg = { element: cloneBlazorObject(this.diagram), position: cloneBlazorObject(this.currentPosition), count: evt.buttons, actualObject: cloneBlazorObject(this.eventArgs.actualObject), button: (evt.button === 0) ? 'Left' : (evt.button === 1) ? 'Middle' : 'Right' }; this.inAction = false; this.tool.mouseUp(this.eventArgs); } else if (((this.inAction === true) && this.isMouseDown === true && (this.tool instanceof PolygonDrawingTool || this.tool instanceof PolyLineDrawingTool))) { this.isMouseDown = true; this.eventArgs = {}; this.getMouseEventArgs(this.currentPosition, this.eventArgs); this.eventArgs.position = this.currentPosition; this.tool.mouseDown(this.eventArgs); } else { this.isMouseDown = true; this.currentPosition = this.prevPosition = this.getMousePosition(evt); this.eventArgs = {}; if (this.diagram.textEditing && !this.isMouseOnScrollBar(evt)) { this.diagram.endEdit(); this.diagram.textEditing = false; // EJ2-57743 - Added below code to refresh the diagram layer after the annotation gets edited in canvas mode. if (this.diagram.mode === 'Canvas' && this.diagram.scroller.currentZoom !== 1) { this.diagram.refreshDiagramLayer(); } } var targetObject = this.getTargetElement(); this.action = this.diagram.findActionToBeDone(targetObject.obj, targetObject.sourceElement, this.currentPosition, targetObject.target); //work around - correct it var ctrlKey = this.isMetaKey(evt); if (ctrlKey && evt.shiftKey && this.diagram.connectorEditingToolModule) { this.action = 'SegmentEnd'; //Bug 892496: Unable to unselect selected node using CTRL+Click when zoompan is enabled. //Added this condition whether single select or multi select is enabled in diagram tool. } else if ((ctrlKey || evt.shiftKey) && (canSingleSelect(this.diagram) || canMultiSelect(this.diagram))) { this.action = 'Select'; } if (this.diagram.constraints & DiagramConstraints.AutomaticPortCreation && evt.ctrlKey && this.eventArgs.actualObject) { this.createPortAutomaticallyAndActivateTool(true); } this.tool = this.diagram.getTool(this.action); if (!this.tool) { this.action = 'Select'; this.tool = this.diagram.getTool(this.action); } this.getMouseEventArgs(this.currentPosition, this.eventArgs); if (ctrlKey || evt.shiftKey) { var info = (ctrlKey && evt.shiftKey) ? { ctrlKey: ctrlKey, shiftKey: evt.shiftKey } : { ctrlKey: true }; this.eventArgs.info = info; } this.eventArgs.position = this.currentPosition; //834641 - Support to unselect the diagram element that is already selected var prevSelectedNode = this.diagram.selectedItems.nodes; this.tool.mouseDown(this.eventArgs); if (this.diagram.selectedItems.canToggleSelection && prevSelectedNode && this.diagram.selectedItems.nodes && this.tool instanceof MoveTool) { for (var i = 0; i < prevSelectedNode.length; i++) { if (prevSelectedNode[parseInt(i.toString(), 10)].id !== this.diagram.selectedItems.nodes[parseInt(i.toString(), 10)].id) { this.isSwimlaneSelected = true; } } } this.initialEventArgs = { source: this.eventArgs.source, sourceWrapper: this.eventArgs.sourceWrapper }; this.initialEventArgs.position = this.currentPosition; this.initialEventArgs.info = this.eventArgs.info; this.inAction = false; if (evt.type === 'touchstart') { if (touches && touches.length >= 2) { this.touchStartList = addTouchPointer(this.touchStartList, evt, touches); } if (!touches) { evt.preventDefault(); } } } } if (!this.isForeignObject(evt.target) && !this.isForeignObject(evt.target, true) && (!touches)) { evt.preventDefault(); } }; DiagramEventHandler.prototype.deletePortAutomatically = function () { if (this.hoverElement instanceof PointPort || this.hoverElement instanceof PathPort) { var port = this.hoverElement; var parentNode = this.diagram.nameTable[port.parentObj.id]; for (var i = 0; i < parentNode.ports.length; i++) { if (parentNode.ports[parseInt(i.toString(), 10)].id === port.id && port.inEdges.length === 0 && port.outEdges.length === 0) { this.diagram.removePorts(parentNode, [port]); } } } }; DiagramEventHandler.prototype.createPortAutomaticallyAndActivateTool = function (isSourceEnd) { var actualTarget = this.findObjectsUnderMouse(this.currentPosition); if (this.hoverElement) { if (this.hoverElement instanceof Node) { var node = this.hoverElement; var nodeBounds = node.wrapper.bounds; var offsetX = (this.currentPosition.x - nodeBounds.x) / nodeBounds.width; var offsetY = (this.currentPosition.y - nodeBounds.y) / nodeBounds.height; var port = void 0; if (node.ports && node.ports.length > 0) { port = cloneObject(node.ports[0]); port.id = 'port' + randomId(); port.offset = { x: offsetX, y: offsetY }; port.constraints |= PortConstraints.Draw; } else { port = { id: 'port' + randomId(), offset: { x: offsetX, y: offsetY }, visibility: PortVisibility.Visible, constraints: PortConstraints.Default | PortConstraints.Draw }; } if (isSourceEnd) { this.diagram.addPorts(node, [port]); var newAction = findPortToolToActivate(this.diagram, node.ports[node.ports.length - 1]); this.action = newAction === 'PortDraw' ? newAction : this.action; } else { if (this.eventArgs.source instanceof Connector && this.eventArgs.target instanceof Node) { var targetNode = this.eventArgs.target; if (targetNode.id === node.id) { this.diagram.addPorts(node, [port]); var drawingConnector = this.eventArgs.source; drawingConnector.targetID = node.id; drawingConnector.targetPortID = port.id; } } else if (this.eventArgs.source instanceof Connector && actualTarget[0] instanceof Connector) { this.addAutomaticPortCreationForConnector(actualTarget, this.eventArgs); } } } else if (this.hoverElement instanceof Connector) { var connector = this.hoverElement; var offset = this.findConnectorOffset(connector.intermediatePoints); var port = void 0; if (connector.ports && connector.ports.length > 0) { port = cloneObject(connector.ports[0]); port.id = 'port' + randomId(); port.offset = offset; port.constraints |= PortConstraints.Draw; } else { port = { id: 'port' + randomId(), offset: offset, visibility: PortVisibility.Visible, constraints: PortConstraints.Default | PortConstraints.Draw }; } if (isSourceEnd) { this.diagram.addPorts(connector, [port]); this.diagram.drawingObject = { sourcePortID: port.id, sourceID: connector.id, type: 'Orthogonal' }; this.action = 'PortDraw'; } else { if (this.eventArgs.source instanceof Connector && this.eventArgs.target instanceof Connector) { var targetConnector = this.eventArgs.target; var drawingConnector = this.eventArgs.source; if (drawingConnector.id !== connector.id && targetConnector.id === connector.id) { this.diagram.addPorts(connector, [port]); drawingConnector.targetID = connector.id; drawingConnector.targetPortID = port.id; } } else if (this.eventArgs.source instanceof Connector && actualTarget[0] instanceof Connector) { this.addAutomaticPortCreationForConnector(actualTarget, this.eventArgs); } } } else if (this.hoverElement instanceof PointPort || this.hoverElement instanceof PathPort) { var port = this.hoverElement; if (port.inEdges.length === 0 && port.outEdges.length === 0) { this.action = 'PortDraw'; } } } }; DiagramEventHandler.prototype.addAutomaticPortCreationForConnector = function (actualTarget, eventArgs) { var connector = actualTarget[0]; var offset = this.findConnectorOffset(connector.intermediatePoints); var port = { id: 'port' + randomId(), offset: offset, visibility: PortVisibility.Visible, constraints: PortConstraints.Default | PortConstraints.Draw }; this.diagram.addPorts(connector, [port]); var drawingConnector = eventArgs.source; drawingConnector.targetID = connector.id; drawingConnector.targetPortID = port.id; }; DiagramEventHandler.prototype.findConnectorOffset = function (points) { var totalLength = 0; var segmentLengths = []; for (var i = 0; i < points.length - 1; i++) { var dx = points[i + 1].x - points[parseInt(i.toString(), 10)].x; var dy = points[i + 1].y - points[parseInt(i.toString(), 10)].y; var segLen = Math.hypot(dx, dy); segmentLengths.push(segLen); totalLength += segLen; } var offsetDistance = 0; var closestDistance = Infinity; for (var j = 0, accLength = 0; j < points.length - 1; j++) { var segmentStart = points[parseInt(j.toString(), 10)]; var segmentEnd = points[j + 1]; var projection = this.getProjectionOnSegment(this.currentPosition, segmentStart, segmentEnd); var distance = Math.hypot(this.currentPosition.x - projection.x, this.currentPosition.y - projection.y); if (distance < closestDistance) { closestDistance = distance; offsetDistance = accLength + Math.hypot(projection.x - segmentStart.x, projection.y - segmentStart.y); } accLength += segmentLengths[parseInt(j.toString(), 10)]; } return offsetDistance / totalLength; }; DiagramEventHandler.prototype.getProjectionOnSegment = function (point, segmentStart, segmentEnd) { var dx = segmentEnd.x - segmentStart.x; var dy = segmentEnd.y - segmentStart.y; var lengthSquared = dx * dx + dy * dy; if (lengthSquared === 0) { return segmentStart; } var projectionFactor = ((point.x - segmentStart.x) * dx + (point.y - segmentStart.y) * dy) / lengthSquared; projectionFactor = Math.max(0, Math.min(1, projectionFactor)); return { x: segmentStart.x + projectionFactor * dx, y: segmentStart.y + projectionFactor * dy }; }; /** @private */ DiagramEventHandler.prototype.mouseMoveExtend = function (e, obj) { if (this.tool instanceof PolygonDrawingTool || this.tool instanceof PolyLineDrawingTool) { this.tool.mouseMove(this.eventArgs); } if (this.diagram.scrollSettings.canAutoScroll) { this.checkAutoScroll(e); } else { if (!this.blocked) { if (!(this.tool instanceof PolygonDrawingTool || this.tool instanceof PolyLineDrawingTool)) { (this.tool.mouseMove(this.eventArgs)); } } } if (this.eventArgs.target) { this.hoverElement = this.eventArgs.target; } var isNode = (this.eventArgs.target instanceof Node || this.eventArgs.target instanceof Connector) && (obj instanceof Node || obj instanceof Connector) ? false : true; if (this.tool instanceof ConnectTool) { this.diagram.updatePortVisibility((this.hoverElement instanceof Node || this.hoverElement instanceof Connector) ? this.hoverElement : this.hoverNode, PortVisibility.Connect | PortVisibility.Hover, isNode); } if (this.hoverElement instanceof Node && this.hoverNode instanceof Node && this.hoverNode && this.hoverNode.id !== this.hoverElement.id) { this.diagram.updatePortVisibility(this.hoverNode, PortVisibility.Connect | PortVisibility.Hover, true); } if (this.hoverElement instanceof Connector && (this.hoverElement.ports.length > 0)) { this.diagram.updatePortVisibility(this.hoverElement, PortVisibility.Connect | PortVisibility.Hover, true); } // 890089: Unintended Port Visibility Issue Fix // Updating port visibility based on the previous hoverElement if (this.previousElement instanceof Node && this.hoverElement && (this.previousElement.id !== this.hoverElement.id)) { this.diagram.updatePortVisibility(this.previousElement, PortVisibility.Connect | PortVisibility.Hover, true); } this.previousElement = this.hoverElement; // Updating tooltip content on mouse relative mode if (this.diagram.tooltip.content !== '') { this.hoverElement = null; } this.hoverNode = isNode ? null : obj; }; /** @private */ DiagramEventHandler.prototype.checkAction = function (obj) { if (this.action === 'LabelSelect' && this.eventArgs.sourceWrapper && (this.eventArgs.sourceWrapper instanceof TextElement || this.eventArgs.sourceWrapper instanceof DiagramHtmlElement)) { var annotation = this.commandHandler.findTarget(this.eventArgs.sourceWrapper, this.eventArgs.source); if (!isSelected(this.diagram, annotation, false, this.eventArgs.sourceWrapper) && canMove(annotation)) { this.action = 'LabelDrag'; this.tool = this.getTool(this.action); this.tool.mouseDown(this.initialEventArgs); } } else if (canMove(obj) && canSelect(obj) && this.initialEventArgs && this.initialEventArgs.source && this.action === 'Select') { if (!isSelected(this.diagram, this.eventArgs.source, false) && this.eventArgs.source instanceof Selector) { this.getMouseEventArgs(this.currentPosition, this.eventArgs); } if (!(obj instanceof Connector) || (obj instanceof Connector && !(contains(this.currentPosition, obj.sourcePoint, obj.hitPadding) || contains(this.currentPosition, obj.targetPoint, obj.hitPadding)))) { this.action = 'Drag'; } this.tool = this.getTool(this.action); this.tool.mouseDown(this.initialEventArgs); } }; DiagramEventHandler.prototype.isSwimlaneElements = function (obj) { if (obj && (obj.isLane || obj.isPhase || obj.isHeader)) { return false; } else { return true; } }; //To decide whether to show tooltip on touch long press DiagramEventHandler.prototype.canShowTouchTooltip = function (evt) { if (evt.type === 'touchstart') { if ((this.diagram.tool & DiagramTools.SingleSelect) && (this.diagram.tool & DiagramTools.ZoomPan)) { this.isMouseDown = false; return true; } } return false; }; /* tslint:disable */ /** @private */ DiagramEventHandler.prototype.mouseMove = function (e, touches) { this.focus = true; //Bug 914365: Node is not resizable using touch interaction if (e.type === 'touchmove') { this.touchArgs = { target: e.target, type: 'touchmove' }; } if (this.isScrolling) { e.preventDefault(); return; } if (canUserInteract(this.diagram) || (canZoomPan(this.diagram) && !defaultTool(this.diagram))) { this.currentPosition = this.getMousePosition(e); var objects = this.diagram.findObjectsUnderMouse(this.currentPosition); var obj = this.diagram.findObjectUnderMouse(objects, this.action, this.inAction); drawRulerMarkers(this.diagram, this.currentPosition); var force = false; var target = void 0; if (e.type === 'touchmove') { touches = e.touches; if (touches && touches.length > 1) { this.touchMoveList = addTouchPointer(this.touchMoveList, e, touches); if (this.action !== 'PinchZoom') { force = true; } } } if (Point.equals(this.currentPosition, this.prevPosition) === false || this.inAction || this.canShowTouchTooltip(e)) { if (this.isMouseDown === false || force) { this.eventArgs = {}; var sourceElement = null; var tooltipTarget = void 0; if (obj !== null) { sourceElement = this.diagram.findElementUnderMouse(obj, this.currentPosition, this.diagram); //834842-Exception/error occurs when hovering on the connector //tooltiptarget to be found only if the source element is not null if (sourceElement) { tooltipTarget = this.commandHandler.findTarget(sourceElement, obj); } //908569: Node Tooltip Flickers when hovering on Annotation //If the annotation does not have tooltip content, set the tooltip target as an object if (tooltipTarget instanceof ShapeAnnotation || tooltipTarget instanceof PathAnnotation) { if (!(tooltipTarget.tooltip && tooltipTarget.tooltip.content !== '')) { tooltipTarget = obj; } } if (tooltipTarget !== this.hoverElement) { var content_1 = this.getContent(); if (this.hoverElement && this.hoverElement.tooltip.openOn === 'Auto' && content_1 !== '') { this.elementLeave(); } this.diagram.updatePortVisibility(this.hoverElement, PortVisibility.Hover, true); if (obj instanceof Node) { this.hoverNode = obj; } var canResetElement = true; if (!this.isSwimlaneElements(obj) && (this.hoverElement && this.isSwimlaneElements(this.hoverElement))) { canResetElement = false; } this.hoverElement = canResetElement ? obj : this.hoverElement; //EJ2-62120 - Provide tooltip support for ports - to set hoverelement as PathElement if hovered on ports in Node var portElement = null; var portTarget = void 0; portElement = this.diagram.findElementUnderMouse(obj, this.currentPosition, this.diagram); if (portElement instanceof PathElement) { portTarget = this.commandHandler.findTarget(portElement, obj); if ((portTarget instanceof PointPort || portTarget instanceof PathPort) && this.hoverElement.constraints & PortConstraints.ToolTip) { this.hoverElement = portTarget; } } //839511: Tooltip support for annotation - to set hoverelement as TextElement if hovered on annotation in Node/Connector var annotationTarget = void 0; if (portElement instanceof TextElement) { annotationTarget = this.commandHandler.findTarget(portElement, obj); if ((annotationTarget instanceof ShapeAnnotation || annotationTarget instanceof PathAnnotation) && annotationTarget.constraints & AnnotationConstraints.Tooltip) { this.hoverElement = annotationTarget; } } if (canResetElement) { this.elementEnter(this.currentPosition, false); } else { this.hoverElement = obj; } } // EJ2-66418 - set tooltip relativeMode as mouse // Updating the tooltip position based on Mouse move else if (this.hoverElement) { if (this.hoverElement === tooltipTarget && this.hoverElement.tooltip.content && this.diagram.tooltipObject !== undefined && this.hoverElement.tooltip.relativeMode === 'Mouse') {