export class DomEvents { private handlers = new Map < { removeEventListener : Function ; } , { [ key ] : Function [ ] ; } > ( ) ; constructor ( private root ) { } public on ( event , selector , handler , root ? ) ; public on ( event , handler , root ? ) ; public on ( ... args ) { const event = args . shift ( ) ; const selector = typeof args [ 0 ] === "string" ? args . shift ( ) : undefined ; const handler = typeof args [ 0 ] === "function" ? args . shift ( ) : ( ) => { } ; const root = args . shift ( ) ; const eventHolder = root || this . root ; if ( ! this . handlers . has ( eventHolder ) ) { this . handlers . set ( eventHolder , { } ) ; } if ( ! this . handlers . get ( eventHolder ) [ event ] ) { this . handlers . get ( eventHolder ) [ event ] = [ ] ; } const evListener = ( ev ) => { let target ; if ( selector ) { const selected = Array . from ( this . root . querySelectorAll ( selector ) ) ; target = ev . target as HTMLElement ; while ( target ) { if ( selected . find ( el => el === target ) ) { break ; } target = target . parentNode ; } if ( ! target ) { return ; } } const handlerOutput = handler ( ev , target || ev . target , this . root ) ; if ( handlerOutput === false ) { return false ; } return false ; } ; eventHolder . addEventListener ( event , evListener ) ; this . handlers . get ( eventHolder ) [ event ] . push ( evListener ) ; return function off ( ) { eventHolder . removeEventListener ( event , evListener ) ; } ; } public keyup ( ) { } public adaptedDrag ( selector , move ? , start ? , end ? ) { let dragging = false ; let lastMove ; let draggedEl ; let moveEventCount = 0 ; let mouseDownEv ; let threshold = 3 ; let mouseOverListeners ; const onMouseDown = ( ev , el ) => { dragging = true ; lastMove = ev ; draggedEl = el ; mouseDownEv = ev ; ev . preventDefault ( ) ; mouseOverListeners = this . detachHandlers ( "mouseover" ) ; document . addEventListener ( "mousemove" , moveHandler ) ; document . addEventListener ( "mouseup" , upHandler ) ; return false ; } ; const off = this . on ( "mousedown" , selector , onMouseDown ) ; const moveHandler = ( ev ) => { if ( ! dragging ) { return ; } const dx = ev . screenX - lastMove . screenX ; const dy = ev . screenY - lastMove . screenY ; moveEventCount ++ ; if ( moveEventCount === threshold && typeof start === "function" ) { start ( mouseDownEv , draggedEl , this . root ) ; } if ( moveEventCount >= threshold && typeof move === "function" ) { move ( dx , dy , ev , draggedEl , this . root ) ; } } ; const upHandler = ( ev ) => { if ( moveEventCount >= threshold ) { if ( dragging ) { if ( typeof end === "function" ) { end ( ev , draggedEl , this . root ) ; } } const parentNode = draggedEl . parentNode ; const clickCancellation = ( ev ) => { ev . stopPropagation ( ) ; parentNode . removeEventListener ( "click" , clickCancellation , true ) ; } ; parentNode . addEventListener ( "click" , clickCancellation , true ) ; } dragging = false ; draggedEl = undefined ; lastMove = undefined ; moveEventCount = 0 ; document . removeEventListener ( "mouseup" , upHandler ) ; document . removeEventListener ( "mousemove" , moveHandler ) ; for ( let i in mouseOverListeners ) { this . root . addEventListener ( "mouseover" , mouseOverListeners [ i ] ) ; this . handlers . get ( this . root ) [ "mouseover" ] = [ ] ; this . handlers . get ( this . root ) [ "mouseover" ] . push ( mouseOverListeners [ i ] ) ; } } ; return off ; } public drag ( selector , move ? , start ? , end ? ) { let dragging = false ; let lastMove ; let draggedEl ; let moveEventCount = 0 ; let mouseDownEv ; let threshold = 3 ; let mouseOverListeners ; const onMouseDown = ( ev , el , root ) => { dragging = true ; lastMove = ev ; draggedEl = el ; mouseDownEv = ev ; ev . preventDefault ( ) ; mouseOverListeners = this . detachHandlers ( "mouseover" ) ; document . addEventListener ( "mousemove" , moveHandler ) ; document . addEventListener ( "mouseup" , upHandler ) ; return false ; } ; const off = this . on ( "mousedown" , selector , onMouseDown ) ; const moveHandler = ( ev ) => { if ( ! dragging ) { return ; } const dx = ev . screenX - lastMove . screenX ; const dy = ev . screenY - lastMove . screenY ; moveEventCount ++ ; if ( moveEventCount === threshold && typeof start === "function" ) { start ( mouseDownEv , draggedEl , this . root ) ; } if ( moveEventCount >= threshold && typeof move === "function" ) { move ( dx , dy , ev , draggedEl , this . root ) ; } } ; const upHandler = ( ev ) => { if ( moveEventCount >= threshold ) { if ( dragging ) { if ( typeof end === "function" ) { end ( ev , draggedEl , this . root ) ; } } if ( draggedEl . contains ( ev . target ) ) { const parentNode = draggedEl . parentNode ; const clickCancellation = ( ev ) => { ev . stopPropagation ( ) ; parentNode . removeEventListener ( "click" , clickCancellation , true ) ; } ; parentNode . addEventListener ( "click" , clickCancellation , true ) ; } } dragging = false ; draggedEl = undefined ; lastMove = undefined ; moveEventCount = 0 ; document . removeEventListener ( "mouseup" , upHandler ) ; document . removeEventListener ( "mousemove" , moveHandler ) ; for ( let i in mouseOverListeners ) { this . root . addEventListener ( "mouseover" , mouseOverListeners [ i ] ) ; this . handlers . get ( this . root ) [ "mouseover" ] = [ ] ; this . handlers . get ( this . root ) [ "mouseover" ] . push ( mouseOverListeners [ i ] ) ; } } ; return off ; } public hover ( element , hover = ( ) => { } , enter = ( ) => { } , leave = ( ) => { } ) { let hovering = false ; element . addEventListener ( "mouseenter" , ( ev ) => { hovering = true ; enter ( ev , element , this . root ) ; } ) ; element . addEventListener ( "mouseleave" , ( ev ) => { hovering = false ; leave ( ev , element , this . root ) ; } ) ; element . addEventListener ( "mousemove" , ( ev ) => { if ( ! hovering ) { return ; } hover ( ev , element , this . root ) ; } ) ; } public detachHandlers ( evName , root ? ) { root = root || this . root ; let eventListeners = [ ] ; this . handlers . forEach ( ( handlers , listenerRoot ) => { if ( listenerRoot . id !== root . id || listenerRoot !== root ) { return ; } for ( let eventName in handlers ) { if ( eventName !== evName ) { continue ; } handlers [ eventName ] . forEach ( ( handler ) => { eventListeners . push ( handler ) ; listenerRoot . removeEventListener ( eventName , handler ) ; } ) ; } } ) ; delete this . handlers . get ( this . root ) [ evName ] ; return eventListeners ; } public detachAll ( ) { this . handlers . forEach ( ( handlers , listenerRoot ) => { for ( let eventName in handlers ) { handlers [ eventName ] . forEach ( handler => listenerRoot . removeEventListener ( eventName , handler ) ) ; } } ) ; this . handlers . clear ( ) ; } }