@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
JavaScript
/* 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') {