@ -3,10 +3,10 @@
*
*
* /
* /
let util = require ( '../../util' ) ;
var util = require ( '../../util' ) ;
import { NavigationHandler } from "./components/NavigationHandler"
import NavigationHandler from "./components/NavigationHandler"
import Popup from "./components/Popup"
class InteractionHandler {
class InteractionHandler {
constructor ( body , canvas , selectionHandler ) {
constructor ( body , canvas , selectionHandler ) {
@ -33,6 +33,9 @@ class InteractionHandler {
this . pinch = { } ;
this . pinch = { } ;
this . pointerPosition = { x : 0 , y : 0 } ;
this . pointerPosition = { x : 0 , y : 0 } ;
this . hoverObj = { nodes : { } , edges : { } } ;
this . hoverObj = { nodes : { } , edges : { } } ;
this . popup = undefined ;
this . popupObj = undefined ;
this . popupTimer = undefined ;
this . options = { } ;
this . options = { } ;
@ -59,12 +62,16 @@ class InteractionHandler {
}
}
}
}
util . extend ( this . options , this . defaultOptions ) ;
util . extend ( this . options , this . defaultOptions ) ;
this . body . emitter . on ( "_dataChanged" , ( ) => {
} )
}
}
setOptions ( options ) {
setOptions ( options ) {
if ( options !== undefined ) {
if ( options !== undefined ) {
// extend all but the values in fields
// extend all but the values in fields
var fields = [ 'keyboard' ] ;
let fields = [ 'keyboard' ] ;
util . selectiveNotDeepExtend ( fields , this . options , options ) ;
util . selectiveNotDeepExtend ( fields , this . options , options ) ;
// merge the keyboard options in.
// merge the keyboard options in.
@ -110,10 +117,10 @@ class InteractionHandler {
* @ private
* @ private
* /
* /
onTap ( event ) {
onTap ( event ) {
var pointer = this . getPointer ( event . center ) ;
let pointer = this . getPointer ( event . center ) ;
var previouslySelected = this . selectionHandler . _getSelectedObjectCount ( ) > 0 ;
var selected = this . selectionHandler . selectOnPoint ( pointer ) ;
let previouslySelected = this . selectionHandler . _getSelectedObjectCount ( ) > 0 ;
let selected = this . selectionHandler . selectOnPoint ( pointer ) ;
if ( selected === true || ( previouslySelected == true && selected === false ) ) { // select or unselect
if ( selected === true || ( previouslySelected == true && selected === false ) ) { // select or unselect
this . body . emitter . emit ( 'select' , this . selectionHandler . getSelection ( ) ) ;
this . body . emitter . emit ( 'select' , this . selectionHandler . getSelection ( ) ) ;
@ -128,7 +135,7 @@ class InteractionHandler {
* @ private
* @ private
* /
* /
onDoubleTap ( event ) {
onDoubleTap ( event ) {
var pointer = this . getPointer ( event . center ) ;
let pointer = this . getPointer ( event . center ) ;
this . selectionHandler . _generateClickEvent ( "doubleClick" , pointer ) ;
this . selectionHandler . _generateClickEvent ( "doubleClick" , pointer ) ;
}
}
@ -139,9 +146,9 @@ class InteractionHandler {
* @ private
* @ private
* /
* /
onHold ( event ) {
onHold ( event ) {
var pointer = this . getPointer ( event . center ) ;
let pointer = this . getPointer ( event . center ) ;
var selectionChanged = this . selectionHandler . selectAdditionalOnPoint ( pointer ) ;
let selectionChanged = this . selectionHandler . selectAdditionalOnPoint ( pointer ) ;
if ( selectionChanged === true ) { // select or longpress
if ( selectionChanged === true ) { // select or longpress
this . body . emitter . emit ( 'select' , this . selectionHandler . getSelection ( ) ) ;
this . body . emitter . emit ( 'select' , this . selectionHandler . getSelection ( ) ) ;
@ -174,7 +181,7 @@ class InteractionHandler {
}
}
// note: drag.pointer is set in onTouch to get the initial touch location
// note: drag.pointer is set in onTouch to get the initial touch location
var node = this . selectionHandler . getNodeAt ( this . drag . pointer ) ;
let node = this . selectionHandler . getNodeAt ( this . drag . pointer ) ;
this . drag . dragging = true ;
this . drag . dragging = true ;
this . drag . selection = [ ] ;
this . drag . selection = [ ] ;
@ -191,12 +198,12 @@ class InteractionHandler {
this . selectionHandler . selectObject ( node ) ;
this . selectionHandler . selectObject ( node ) ;
}
}
var selection = this . selectionHandler . selectionObj . nodes ;
let selection = this . selectionHandler . selectionObj . nodes ;
// create an array with the selected nodes and their original location and status
// create an array with the selected nodes and their original location and status
for ( let nodeId in selection ) {
for ( let nodeId in selection ) {
if ( selection . hasOwnProperty ( nodeId ) ) {
if ( selection . hasOwnProperty ( nodeId ) ) {
var object = selection [ nodeId ] ;
var s = {
let object = selection [ nodeId ] ;
let s = {
id : object . id ,
id : object . id ,
node : object ,
node : object ,
@ -229,16 +236,16 @@ class InteractionHandler {
// remove the focus on node if it is focussed on by the focusOnNode
// remove the focus on node if it is focussed on by the focusOnNode
this . body . emitter . emit ( "unlockNode" ) ;
this . body . emitter . emit ( "unlockNode" ) ;
var pointer = this . getPointer ( event . center ) ;
var selection = this . drag . selection ;
let pointer = this . getPointer ( event . center ) ;
let selection = this . drag . selection ;
if ( selection && selection . length && this . options . dragNodes === true ) {
if ( selection && selection . length && this . options . dragNodes === true ) {
// calculate delta's and new location
// calculate delta's and new location
var deltaX = pointer . x - this . drag . pointer . x ;
var deltaY = pointer . y - this . drag . pointer . y ;
let deltaX = pointer . x - this . drag . pointer . x ;
let deltaY = pointer . y - this . drag . pointer . y ;
// update position of all selected nodes
// update position of all selected nodes
selection . forEach ( ( selection ) => {
selection . forEach ( ( selection ) => {
var node = selection . node ;
let node = selection . node ;
// only move the node if it was not fixed initially
// only move the node if it was not fixed initially
if ( selection . xFixed === false ) {
if ( selection . xFixed === false ) {
node . x = this . canvas . _XconvertDOMtoCanvas ( this . canvas . _XconvertCanvasToDOM ( selection . x ) + deltaX ) ;
node . x = this . canvas . _XconvertDOMtoCanvas ( this . canvas . _XconvertCanvasToDOM ( selection . x ) + deltaX ) ;
@ -261,8 +268,8 @@ class InteractionHandler {
this . _handleDragStart ( event ) ;
this . _handleDragStart ( event ) ;
return ;
return ;
}
}
var diffX = pointer . x - this . drag . pointer . x ;
var diffY = pointer . y - this . drag . pointer . y ;
let diffX = pointer . x - this . drag . pointer . x ;
let diffY = pointer . y - this . drag . pointer . y ;
this . body . view . translation = { x : this . drag . translation . x + diffX , y : this . drag . translation . y + diffY } ;
this . body . view . translation = { x : this . drag . translation . x + diffX , y : this . drag . translation . y + diffY } ;
this . body . emitter . emit ( "_redraw" ) ;
this . body . emitter . emit ( "_redraw" ) ;
@ -277,7 +284,7 @@ class InteractionHandler {
* /
* /
onDragEnd ( event ) {
onDragEnd ( event ) {
this . drag . dragging = false ;
this . drag . dragging = false ;
var selection = this . drag . selection ;
let selection = this . drag . selection ;
if ( selection && selection . length ) {
if ( selection && selection . length ) {
selection . forEach ( function ( s ) {
selection . forEach ( function ( s ) {
// restore original xFixed and yFixed
// restore original xFixed and yFixed
@ -301,7 +308,7 @@ class InteractionHandler {
* @ private
* @ private
* /
* /
onPinch ( event ) {
onPinch ( event ) {
var pointer = this . getPointer ( event . center ) ;
let pointer = this . getPointer ( event . center ) ;
this . drag . pinched = true ;
this . drag . pinched = true ;
if ( this . pinch [ 'scale' ] === undefined ) {
if ( this . pinch [ 'scale' ] === undefined ) {
@ -309,7 +316,7 @@ class InteractionHandler {
}
}
// TODO: enabled moving while pinching?
// TODO: enabled moving while pinching?
var scale = this . pinch . scale * event . scale ;
let scale = this . pinch . scale * event . scale ;
this . zoom ( scale , pointer )
this . zoom ( scale , pointer )
}
}
@ -323,7 +330,7 @@ class InteractionHandler {
* /
* /
zoom ( scale , pointer ) {
zoom ( scale , pointer ) {
if ( this . options . zoomView === true ) {
if ( this . options . zoomView === true ) {
var scaleOld = this . body . view . scale ;
let scaleOld = this . body . view . scale ;
if ( scale < 0.00001 ) {
if ( scale < 0.00001 ) {
scale = 0.00001 ;
scale = 0.00001 ;
}
}
@ -331,24 +338,24 @@ class InteractionHandler {
scale = 10 ;
scale = 10 ;
}
}
var preScaleDragPointer = null ;
let preScaleDragPointer = null ;
if ( this . drag !== undefined ) {
if ( this . drag !== undefined ) {
if ( this . drag . dragging === true ) {
if ( this . drag . dragging === true ) {
preScaleDragPointer = this . canvas . DOMtoCanvas ( this . drag . pointer ) ;
preScaleDragPointer = this . canvas . DOMtoCanvas ( this . drag . pointer ) ;
}
}
}
}
// + this.canvas.frame.canvas.clientHeight / 2
// + this.canvas.frame.canvas.clientHeight / 2
var translation = this . body . view . translation ;
let translation = this . body . view . translation ;
var scaleFrac = scale / scaleOld ;
var tx = ( 1 - scaleFrac ) * pointer . x + translation . x * scaleFrac ;
var ty = ( 1 - scaleFrac ) * pointer . y + translation . y * scaleFrac ;
let scaleFrac = scale / scaleOld ;
let tx = ( 1 - scaleFrac ) * pointer . x + translation . x * scaleFrac ;
let ty = ( 1 - scaleFrac ) * pointer . y + translation . y * scaleFrac ;
this . body . view . scale = scale ;
this . body . view . scale = scale ;
this . body . view . translation = { x : tx , y : ty } ;
this . body . view . translation = { x : tx , y : ty } ;
if ( preScaleDragPointer != null ) {
if ( preScaleDragPointer != null ) {
var postScaleDragPointer = this . canvas . canvasToDOM ( preScaleDragPointer ) ;
let postScaleDragPointer = this . canvas . canvasToDOM ( preScaleDragPointer ) ;
this . drag . pointer . x = postScaleDragPointer . x ;
this . drag . pointer . x = postScaleDragPointer . x ;
this . drag . pointer . y = postScaleDragPointer . y ;
this . drag . pointer . y = postScaleDragPointer . y ;
}
}
@ -374,7 +381,7 @@ class InteractionHandler {
* /
* /
onMouseWheel ( event ) {
onMouseWheel ( event ) {
// retrieve delta
// retrieve delta
var delta = 0 ;
let delta = 0 ;
if ( event . wheelDelta ) { /* IE/Opera. */
if ( event . wheelDelta ) { /* IE/Opera. */
delta = event . wheelDelta / 120 ;
delta = event . wheelDelta / 120 ;
} else if ( event . detail ) { /* Mozilla case. */
} else if ( event . detail ) { /* Mozilla case. */
@ -389,15 +396,15 @@ class InteractionHandler {
if ( delta ) {
if ( delta ) {
// calculate the new scale
// calculate the new scale
var scale = this . body . view . scale ;
var zoom = delta / 10 ;
let scale = this . body . view . scale ;
let zoom = delta / 10 ;
if ( delta < 0 ) {
if ( delta < 0 ) {
zoom = zoom / ( 1 - zoom ) ;
zoom = zoom / ( 1 - zoom ) ;
}
}
scale *= ( 1 + zoom ) ;
scale *= ( 1 + zoom ) ;
// calculate the pointer location
// calculate the pointer location
var pointer = { x : event . pageX , y : event . pageY } ;
let pointer = { x : event . pageX , y : event . pageY } ;
// apply the new scale
// apply the new scale
this . zoom ( scale , pointer ) ;
this . zoom ( scale , pointer ) ;
@ -414,76 +421,212 @@ class InteractionHandler {
* @ private
* @ private
* /
* /
onMouseMove ( event ) {
onMouseMove ( event ) {
// var pointer = {x:event.pageX, y:event.pageY};
// var popupVisible = false;
//
// // check if the previously selected node is still selected
// if (this.popup !== undefined) {
// if (this.popup.hidden === false) {
// this._checkHidePopup(pointer);
// }
//
// // if the popup was not hidden above
// if (this.popup.hidden === false) {
// popupVisible = true;
// this.popup.setPosition(pointer.x + 3, pointer.y - 5)
// this.popup.show();
// }
// }
//
// // if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over
// if (this.options.keyboard.bindToWindow == false && this.options.keyboard.enabled === true) {
// this.canvas.frame.focus();
// }
//
// // start a timeout that will check if the mouse is positioned above an element
// if (popupVisible === false) {
// var me = this;
// var checkShow = function() {
// me._checkShowPopup(pointer);
// };
//
// if (this.popupTimer) {
// clearInterval(this.popupTimer); // stop any running calculationTimer
// }
// if (!this.drag.dragging) {
// this.popupTimer = setTimeout(checkShow, this.options.tooltip.delay);
// }
// }
//
// /**
// * Adding hover highlights
// */
// if (this.options.hoverEnabled === true) {
// // removing all hover highlights
// for (var edgeId in this.hoverObj.edges) {
// if (this.hoverObj.edges.hasOwnProperty(edgeId)) {
// this.hoverObj.edges[edgeId].hover = false;
// delete this.hoverObj.edges[edgeId];
// }
// }
//
// // adding hover highlights
// var obj = this.selectionHandler.getNodeAt(pointer);
// if (obj == null) {
// obj = this.selectionHandler.getEdgeAt(pointer);
// }
// if (obj != null) {
// this._hoverObject(obj);
// }
//
// // removing all node hover highlights except for the selected one.
// for (var nodeId in this.hoverObj.nodes) {
// if (this.hoverObj.nodes.hasOwnProperty(nodeId)) {
// if (obj instanceof Node && obj.id != nodeId || obj instanceof Edge || obj == null) {
// this._blurObject(this.hoverObj.nodes[nodeId]);
// delete this.hoverObj.nodes[nodeId];
// }
// }
// }
// this.body.emitter.emit("_requestRedraw");
// }
let pointer = { x : event . pageX , y : event . pageY } ;
let popupVisible = false ;
// check if the previously selected node is still selected
if ( this . popup !== undefined ) {
if ( this . popup . hidden === false ) {
this . _checkHidePopup ( pointer ) ;
}
// if the popup was not hidden above
if ( this . popup . hidden === false ) {
popupVisible = true ;
this . popup . setPosition ( pointer . x + 3 , pointer . y - 5 )
this . popup . show ( ) ;
}
}
// if we bind the keyboard to the div, we have to highlight it to use it. This highlights it on mouse over.
if ( this . options . keyboard . bindToWindow == false && this . options . keyboard . enabled === true ) {
this . canvas . frame . focus ( ) ;
}
// start a timeout that will check if the mouse is positioned above an element
if ( popupVisible === false ) {
if ( this . popupTimer !== undefined ) {
clearInterval ( this . popupTimer ) ; // stop any running calculationTimer
this . popupTimer = undefined ;
}
if ( ! this . drag . dragging ) {
this . popupTimer = setTimeout ( ( ) => this . _checkShowPopup ( pointer ) , this . options . tooltip . delay ) ;
}
}
/ * *
* Adding hover highlights
* /
if ( this . options . hoverEnabled === true ) {
// removing all hover highlights
for ( let edgeId in this . hoverObj . edges ) {
if ( this . hoverObj . edges . hasOwnProperty ( edgeId ) ) {
this . hoverObj . edges [ edgeId ] . hover = false ;
delete this . hoverObj . edges [ edgeId ] ;
}
}
// adding hover highlights
let obj = this . selectionHandler . getNodeAt ( pointer ) ;
if ( obj == null ) {
obj = this . selectionHandler . getEdgeAt ( pointer ) ;
}
if ( obj != null ) {
this . selectionHandler . hoverObject ( obj ) ;
}
// removing all node hover highlights except for the selected one.
for ( let nodeId in this . hoverObj . nodes ) {
if ( this . hoverObj . nodes . hasOwnProperty ( nodeId ) ) {
if ( obj instanceof Node && obj . id != nodeId || obj instanceof Edge || obj == null ) {
this . selectionHandler . blurObject ( this . hoverObj . nodes [ nodeId ] ) ;
delete this . hoverObj . nodes [ nodeId ] ;
}
}
}
this . body . emitter . emit ( "_requestRedraw" ) ;
}
}
}
/ * *
* Check if there is an element on the given position in the network
* ( a node or edge ) . If so , and if this element has a title ,
* show a popup window with its title .
*
* @ param { { x : Number , y : Number } } pointer
* @ private
* /
_checkShowPopup ( pointer ) {
let x = this . canvas . _XconvertDOMtoCanvas ( pointer . x ) ;
let y = this . canvas . _YconvertDOMtoCanvas ( pointer . y ) ;
let pointerObj = {
left : x ,
top : y ,
right : x ,
bottom : y
} ;
let previousPopupObjId = this . popupObj === undefined ? "" : this . popupObj . id ;
let nodeUnderCursor = false ;
let popupType = "node" ;
// check if a node is under the cursor.
if ( this . popupObj === undefined ) {
// search the nodes for overlap, select the top one in case of multiple nodes
let nodeIndices = this . body . nodeIndices ;
let nodes = this . body . nodes ;
let node ;
let overlappingNodes = [ ] ;
for ( let i = 0 ; i < nodeIndices . length ; i ++ ) {
node = nodes [ nodeIndices [ i ] ] ;
if ( node . isOverlappingWith ( pointerObj ) === true ) {
if ( node . getTitle ( ) !== undefined ) {
overlappingNodes . push ( nodeIndices [ i ] ) ;
}
}
}
if ( overlappingNodes . length > 0 ) {
// if there are overlapping nodes, select the last one, this is the one which is drawn on top of the others
this . popupObj = nodes [ overlappingNodes [ overlappingNodes . length - 1 ] ] ;
// if you hover over a node, the title of the edge is not supposed to be shown.
nodeUnderCursor = true ;
}
}
if ( this . popupObj === undefined && nodeUnderCursor == false ) {
// search the edges for overlap
let edgeIndices = this . body . edgeIndices ;
let edges = this . body . edges ;
let edge ;
let overlappingEdges = [ ] ;
for ( let i = 0 ; i < edgeIndices . length ; i ++ ) {
edge = edges [ edgeIndices [ i ] ] ;
if ( edge . isOverlappingWith ( pointerObj ) === true ) {
if ( edge . connected === true && edge . getTitle ( ) !== undefined ) {
overlappingEdges . push ( edgeIndices [ i ] ) ;
}
}
}
if ( overlappingEdges . length > 0 ) {
this . popupObj = edges [ overlappingEdges [ overlappingEdges . length - 1 ] ] ;
popupType = "edge" ;
}
}
if ( this . popupObj !== undefined ) {
// show popup message window
if ( this . popupObj . id != previousPopupObjId ) {
if ( this . popup === undefined ) {
this . popup = new Popup ( this . frame , this . options . tooltip ) ;
}
this . popup . popupTargetType = popupType ;
this . popup . popupTargetId = this . popupObj . id ;
// adjust a small offset such that the mouse cursor is located in the
// bottom left location of the popup, and you can easily move over the
// popup area
this . popup . setPosition ( pointer . x + 3 , pointer . y - 5 ) ;
this . popup . setText ( this . popupObj . getTitle ( ) ) ;
this . popup . show ( ) ;
}
}
else {
if ( this . popup ) {
this . popup . hide ( ) ;
}
}
}
/ * *
* Check if the popup must be hidden , which is the case when the mouse is no
* longer hovering on the object
* @ param { { x : Number , y : Number } } pointer
* @ private
* /
_checkHidePopup ( pointer ) {
let x = this . canvas . _XconvertDOMtoCanvas ( pointer . x ) ;
let y = this . canvas . _YconvertDOMtoCanvas ( pointer . y ) ;
let pointerObj = {
left : x ,
top : y ,
right : x ,
bottom : y
} ;
let stillOnObj = false ;
if ( this . popup . popupTargetType == 'node' ) {
if ( this . body . nodes [ this . popup . popupTargetId ] !== undefined ) {
stillOnObj = this . body . nodes [ this . popup . popupTargetId ] . isOverlappingWith ( pointerObj ) ;
// if the mouse is still one the node, we have to check if it is not also on one that is drawn on top of it.
// we initially only check stillOnObj because this is much faster.
if ( stillOnObj === true ) {
let overNode = this . selectionHandler . getNodeAt ( pointer ) ;
stillOnObj = overNode . id == this . popup . popupTargetId ;
}
}
}
else {
if ( this . selectionHandler . getNodeAt ( pointer ) === null ) {
if ( this . body . edges [ this . popup . popupTargetId ] !== undefined ) {
stillOnObj = this . body . edges [ this . popup . popupTargetId ] . isOverlappingWith ( pointerObj ) ;
}
}
}
if ( stillOnObj === false ) {
this . popupObj = undefined ;
this . popup . hide ( ) ;
}
}
}
}
export default InteractionHandler ;
export default InteractionHandler ;