library-ts/browser/dom/EventListeners.ts

967 lines
24 KiB
TypeScript

import { Func } from "../tools/TypeUtilities";
import { Cursor } from "./Cursor";
import { DOMHitTest } from "./DOMHitTest";
export type click = "click";
export type contextmenu = "contextmenu";
export type touchstart = "touchstart";
export type touchmove = "touchmove";
export type touchcancel = "touchcancel";
export type touchend = "touchend";
export type mousedown = "mousedown";
export type mousemove = "mousemove";
export type mouseup = "mouseup";
export type mouseleave = "mouseleave";
export type dblclick = "dblclick";
export type mouseover = "mouseover";
export type mouseenter = "mouseenter";
export type wheel = "wheel";
export type keydown = "keydown";
export type keyup = "keyup";
export type blur = "blur";
export type focus = "focus";
export type change = "change";
export type input = "input";
export type submit = "submit";
export type resize = "resize";
export type pause = "pause";
export type play = "play";
export type ended = "ended";
export type timeupdate = "timeupdate";
export type pointerlockchange = "pointerlockchange";
export type pointerlockerror = "pointerlockerror";
export type load = "load";
export type selectstart = "selectstart";
export type hashchange = "hashchange";
export type visibilitychange = "visibilitychange";
// export type (\w+).+
// $1 |
export type EventListenerType =
click |
contextmenu |
touchstart |
touchmove |
touchcancel |
touchend |
mousedown |
mousemove |
mouseup |
mouseleave |
dblclick |
mouseover |
mouseenter |
wheel |
keydown |
keyup |
blur |
focus |
change |
input |
submit |
resize |
pause |
play |
ended |
timeupdate |
pointerlockchange |
pointerlockerror |
load |
selectstart |
hashchange |
visibilitychange
;
// export type (\w+)\s*=\s*"(.+)"\s*;
// static readonly $1:$1 = "$2";
export class Events
{
static readonly MouseButtonLeft = 0;
static readonly MouseButtonMiddle = 1;
static readonly MouseButtonRight = 2;
static readonly click:click = "click";
static readonly contextmenu:contextmenu = "contextmenu";
static readonly touchstart:touchstart = "touchstart";
static readonly touchmove:touchmove = "touchmove";
static readonly touchcancel:touchcancel = "touchcancel";
static readonly touchend:touchend = "touchend";
static readonly mousedown:mousedown = "mousedown";
static readonly mousemove:mousemove = "mousemove";
static readonly mouseup:mouseup = "mouseup";
static readonly mouseleave:mouseleave = "mouseleave";
static readonly dblclick:dblclick = "dblclick";
static readonly mouseover:mouseover = "mouseover";
static readonly mouseenter:mouseenter = "mouseenter";
static readonly wheel:wheel = "wheel";
static readonly keydown:keydown = "keydown";
static readonly keyup:keyup = "keyup";
static readonly blur:blur = "blur";
static readonly focus:focus = "focus";
static readonly change:change = "change";
static readonly input:input = "input";
static readonly submit:submit = "submit";
static readonly resize:resize = "resize";
static readonly pause:pause = "pause";
static readonly play:play = "play";
static readonly ended:ended = "ended";
static readonly timeupdate:timeupdate = "timeupdate";
static readonly pointerlockchange:pointerlockchange = "pointerlockchange";
static readonly pointerlockerror:pointerlockerror = "pointerlockerror";
static readonly load = "load";
static readonly selectstart:selectstart = "selectstart";
static readonly hashchange:hashchange = "hashchange";
static readonly visibilitychange:visibilitychange = "visibilitychange";
static copyMouseEvent( e:MouseEvent )
{
return new MouseEvent( e.type, e );
}
static copyTouchEvent( e:TouchEvent )
{
let te = new TouchEvent( e.type, e as any );
return te;
}
static copyMouseOrTouchEvent( e:TouchEvent|MouseEvent )
{
if ( this.isMouseEvent( e ) )
{
return this.copyMouseEvent( e as MouseEvent );
}
return this.copyTouchEvent( e as TouchEvent );
}
static is( e:Event, ...types:EventListenerType[] )
{
for ( let t of types )
{
if ( e.type === t )
{
return true;
}
}
return false;
}
static isType( e:string, ...types:EventListenerType[] )
{
for ( let t of types )
{
if ( e === t )
{
return true;
}
}
return false;
}
static isEventOneOf( e:string|Event, ...types:EventListenerType[] )
{
if ( typeof e === "string" )
{
for ( let t of types )
{
if ( e === t )
{
return true;
}
}
return false;
}
for ( let t of types )
{
if ( e.type === t )
{
return true;
}
}
return false;
}
static isMouseEvent( e:MouseEvent|TouchEvent )
{
return /^mouse/.test( e.type ) || ( e.type === Events.dblclick );
}
static îsTouchEvent( e:MouseEvent|TouchEvent )
{
return /^touch/.test( e.type )
}
static toMouseEvent( e:MouseEvent|TouchEvent )
{
if ( ! this.isMouseEvent( e ) )
{
console.log( "Not mouse event:", e.type );
return null;
}
return e as MouseEvent;
}
static toTouchEvent( e:MouseEvent|TouchEvent )
{
if ( this.îsTouchEvent( e ) )
{
return null;
}
return e as TouchEvent;
}
static isStart( e:MouseEvent|TouchEvent|string )
{
return Events.isEventOneOf( e, Events.mousedown, Events.touchstart );
}
static isMove( e:MouseEvent|TouchEvent|string )
{
return Events.isEventOneOf( e, Events.mousemove, Events.touchmove );
}
static isEnd( e:MouseEvent|TouchEvent|string )
{
return Events.isEventOneOf( e, Events.mouseup, Events.touchend, Events.touchcancel );
}
static silent( e:Event )
{
e.stopImmediatePropagation();
e.stopPropagation();
e.preventDefault();
}
static once<T>( target:EventTarget, type:EventListenerType, callback:(t:T)=>void )
{
let onceCallback = ( e:T )=>
{
try
{
callback( e );
}
catch( ex )
{
console.error( ex );
}
target.removeEventListener( type, onceCallback as any );
}
target.addEventListener( type, onceCallback as any );
}
static disableSelection( e:HTMLElement ):Func<void>
{
let preventer = ( ev:any ) => { ev.preventDefault() };
OnMouseDown.add( e, preventer );
OnSelectStart.add( e, preventer );
let remover = ()=>
{
OnMouseDown.remove( e, preventer );
OnSelectStart.remove( e, preventer );
}
return remover;
}
}
export class OnPointerLockChange
{
static add( element:HTMLElement|Document, callback:( e:Event )=>void, options:any = undefined )
{
document.addEventListener( Events.pointerlockchange, callback, options );
}
static remove( element:HTMLElement|Document, callback:( e:Event )=>void )
{
document.removeEventListener( Events.pointerlockchange, callback );
}
}
export class OnWheel
{
static add( element:HTMLElement, callback:( e:WheelEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.wheel, callback, options );
}
static remove( element:HTMLElement, callback:( e:WheelEvent )=>void )
{
element.removeEventListener( Events.wheel, callback );
}
}
export type MouseEventCallback = ( e:MouseEvent )=>void;
export class WrappedCallback
{
element:HTMLElement;
wrappedCallback:MouseEventCallback;
}
export class OnClick
{
static add( element:HTMLElement, callback:MouseEventCallback, options:any = undefined )
{
element.addEventListener( Events.click, callback, options );
}
static remove( element:HTMLElement, callback:MouseEventCallback )
{
element.removeEventListener( Events.click, callback );
}
}
export class OnMouseDown
{
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.mousedown, callback, options );
}
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
{
element.removeEventListener( Events.mousedown, callback );
}
}
export class OnContextMenu
{
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.contextmenu, callback, options );
}
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
{
element.removeEventListener( Events.contextmenu, callback );
}
}
export class OnMouseDrag
{
static noHold = false;
static add( element:HTMLElement, callback:( e:MouseEvent )=>void|boolean, options:any = undefined )
{
let noHold = OnMouseDrag.noHold;
let isHoldInteraction = null;
let downStart = 0;
let holdInteractionTreshold = 500;
let checkAutomaticSwitch = (e:MouseEvent) =>
{
let time = new Date().getTime();
let elapsed = time - downStart;
if ( elapsed > holdInteractionTreshold )
{
removeEvents( e );
}
else
{
Cursor.lock();
//UIComponent.lockCursor( UICursors.releaseDragging + "" );
}
}
let removeEvents = ( e:MouseEvent )=>
{
if ( noHold )
{
let mouseUpEvent = new MouseEvent( Events.mouseup, e );
callback( mouseUpEvent );
}
else
{
callback( e );
}
OnMouseMove.remove( document.body, callback );
if ( noHold )
{
OnMouseUp.remove( document.body, checkAutomaticSwitch );
OnMouseDown.remove( document.body, removeEvents );
//UIComponent.freeCursor();
Cursor.free();
}
else
{
OnMouseUp.remove( document.body, removeEvents );
}
}
let addEvents = ()=>
{
OnMouseMove.add( document.body, callback );
if ( noHold )
{
OnMouseUp.add( document.body, checkAutomaticSwitch );
OnMouseDown.add( document.body, removeEvents );
}
else
{
OnMouseUp.add( document.body, removeEvents );
}
}
let onDown = ( e:MouseEvent )=>
{
let result = callback( e );
if ( result === false )
{
return;
}
downStart = new Date().getTime();
isHoldInteraction = true;
addEvents();
}
OnMouseDown.add( element, onDown );
return onDown;
}
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
{
OnMouseDown.remove( element, callback );
}
}
export class OnMouseOver
{
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.mouseover, callback, options );
}
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
{
element.removeEventListener( Events.mouseover, callback );
}
}
export class OnMouseEnter
{
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.mouseenter, callback, options );
}
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
{
element.removeEventListener( Events.mouseenter, callback );
}
}
export class OnDoubleClick
{
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.dblclick, callback, options );
}
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
{
element.removeEventListener( Events.dblclick, callback );
}
}
export class OnMouseMove
{
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.mousemove, callback, options );
}
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
{
element.removeEventListener( Events.mousemove, callback );
}
}
export class OnMouseUp
{
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.mouseup, callback, options );
}
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
{
element.removeEventListener( Events.mouseup, callback );
}
}
export class OnMouseLeave
{
static add( element:HTMLElement, callback:( e:MouseEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.mouseleave, callback, options );
}
static remove( element:HTMLElement, callback:( e:MouseEvent )=>void )
{
element.removeEventListener( Events.mouseleave, callback );
}
}
export class OnKeyDown
{
static add( element:HTMLElement, callback:( e:KeyboardEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.keydown, callback, options );
}
static remove( element:HTMLElement, callback:( e:KeyboardEvent )=>void )
{
element.removeEventListener( Events.keydown, callback );
}
}
export class OnKeyUp
{
static add( element:HTMLElement, callback:( e:KeyboardEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.keyup, callback, options );
}
static remove( element:HTMLElement, callback:( e:KeyboardEvent )=>void )
{
element.removeEventListener( Events.keyup, callback );
}
}
export class OnTouchStart
{
static add( element:HTMLElement, callback:( e:TouchEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.touchstart, callback, options );
}
static remove( element:HTMLElement, callback:( e:TouchEvent )=>void )
{
element.removeEventListener( Events.touchstart, callback );
}
}
export class OnTouchEnd
{
static add( element:HTMLElement, callback:( e:TouchEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.touchend, callback, options );
}
static remove( element:HTMLElement, callback:( e:TouchEvent )=>void )
{
element.removeEventListener( Events.touchend, callback );
}
}
export class OnTouchCancel
{
static add( element:HTMLElement, callback:( e:TouchEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.touchcancel, callback, options );
}
static remove( element:HTMLElement, callback:( e:TouchEvent )=>void )
{
element.removeEventListener( Events.touchcancel, callback );
}
}
export class OnTouchMove
{
static add( element:HTMLElement, callback:( e:TouchEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.touchmove, callback, options );
}
static remove( element:HTMLElement, callback:( e:TouchEvent )=>void )
{
element.removeEventListener( Events.touchmove, callback );
}
}
export class OnTouchDrag
{
static add( element:HTMLElement, callback:( e:TouchEvent )=>void, options:any = undefined )
{
OnTouchStart.add( element, callback, options );
OnTouchMove.add( element, callback, options );
OnTouchEnd.add( element, callback, options );
}
static remove( element:HTMLElement, callback:( e:TouchEvent )=>void )
{
OnTouchStart.remove( element, callback );
OnTouchMove.remove( element, callback );
OnTouchEnd.remove( element, callback );
}
}
export class OnDrag
{
static add( element:HTMLElement, callback:( e:TouchEvent|MouseEvent )=>void, options:any = undefined )
{
OnTouchDrag.add( element, callback );
let mouseCallback = OnMouseDrag.add( element, callback );
return { touch:callback, mouse:mouseCallback };
}
static remove( element:HTMLElement, callbackData:{ touch:()=>void, mouse:()=>void } )
{
OnTouchDrag.add( element, callbackData.touch );
OnMouseDrag.add( element, callbackData.mouse );
}
}
export type HORIZONTAL = "HORIZONTAL";
export type VERTICAL = "VERTICAL";
export type BOTH = "BOTH";
export type SwipeType = HORIZONTAL | VERTICAL | BOTH;
export class OnSwipe
{
static readonly HORIZONTAL:HORIZONTAL = "HORIZONTAL";
static readonly VERTICAL:VERTICAL = "VERTICAL";
static readonly BOTH:BOTH = "BOTH";
static add( type:SwipeType, treshold:number, element:HTMLElement, callback:( e:TouchEvent )=>void, options:any = undefined )
{
let onStart =
( startEvent:TouchEvent )=>
{
let checkingMovement = true;
let active = false;
let startPosition = DOMHitTest.getPointerPosition( startEvent );
let onDrag = ( dragEvent:TouchEvent )=>
{
if ( checkingMovement )
{
let position = DOMHitTest.getPointerPosition( dragEvent );
let difference = position.sub( startPosition );
let moved = difference.length > treshold;
if ( moved )
{
if ( OnSwipe.HORIZONTAL === type )
{
active = Math.abs( difference.x ) > Math.abs( difference.y );
}
else if ( OnSwipe.VERTICAL === type )
{
active = Math.abs( difference.y ) > Math.abs( difference.x );
}
else
{
active = true;
}
checkingMovement = false;
callback( startEvent );
}
}
if ( ! active )
{
return;
}
callback( dragEvent );
dragEvent.preventDefault();
dragEvent.stopImmediatePropagation();
dragEvent.stopPropagation();
}
let removeListeners = ( endEvent :TouchEvent ) =>
{
if ( active )
{
callback( endEvent );
endEvent.preventDefault();
endEvent.stopImmediatePropagation();
endEvent.stopPropagation();
}
OnTouchMove.remove( element, onDrag );
OnTouchCancel.remove( element, removeListeners );
OnTouchEnd.remove( element, removeListeners );
}
OnTouchMove.add( element, onDrag );
OnTouchCancel.add( element, removeListeners );
OnTouchEnd.add( element, removeListeners );
};
OnTouchStart.add( element, onStart );
return onStart;
}
static remove( element:HTMLElement, callback:( e:TouchEvent )=>void )
{
OnTouchStart.remove( element, callback );
}
}
export class OnBlur
{
static add( element:HTMLElement, callback:( e:FocusEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.blur, callback, options );
}
static remove( element:HTMLElement, callback:( e:FocusEvent )=>void )
{
element.removeEventListener( Events.blur, callback );
}
}
export class OnFocus
{
static add( element:HTMLElement, callback:( e:FocusEvent )=>void, options:any = undefined )
{
element.addEventListener( Events.focus, callback, options );
}
static remove( element:HTMLElement, callback:( e:FocusEvent )=>void )
{
element.removeEventListener( Events.focus, callback );
}
}
export class OnChange
{
static add( element:HTMLElement|HTMLInputElement, callback:( e:Event )=>void, options:any = undefined )
{
element.addEventListener( Events.change, callback, options );
}
static remove( element:HTMLElement|HTMLInputElement, callback:( e:Event )=>void )
{
element.removeEventListener( Events.change, callback );
}
}
export class OnHashChange
{
static add( window:Window, callback:( e:Event )=>void, options:any = undefined )
{
window.addEventListener( Events.hashchange, callback, options );
}
static remove( window:Window, callback:( e:Event )=>void )
{
window.removeEventListener( Events.hashchange, callback );
}
}
export class OnVisibilityChange
{
static add( document:Document, callback:( e:Event )=>void, options:any = undefined )
{
window.addEventListener( Events.visibilitychange, callback, options );
}
static remove( document:Document, callback:( e:Event )=>void )
{
window.removeEventListener( Events.visibilitychange, callback );
}
}
export class OnInput
{
static add( element:HTMLElement, callback:( e:InputEvent )=>void, options:any = undefined )
{
( element as any ).addEventListener( Events.input, callback, options );
}
static remove( element:HTMLElement, callback:( e:InputEvent )=>void )
{
( element as any ).removeEventListener( Events.input, callback );
}
}
export class OnSubmit
{
static add( element:HTMLFormElement, callback:( e:Event )=>void, options:any = undefined )
{
element.addEventListener( Events.submit, callback, options );
}
static remove( element:HTMLFormElement, callback:( e:Event )=>void )
{
element.removeEventListener( Events.submit, callback );
}
}
export class OnResize
{
static add( window:Window, callback:( e:Event )=>void, options:any = undefined )
{
window.addEventListener( Events.resize, callback, options );
}
static remove( window:Window, callback:( e:Event )=>void )
{
window.removeEventListener( Events.resize, callback );
}
}
export class OnPlay
{
static add( audioElement:HTMLAudioElement, callback:( e:Event )=>void, options:any = undefined )
{
audioElement.addEventListener( Events.play, callback, options );
}
static remove( audioElement:HTMLAudioElement, callback:( e:Event )=>void )
{
audioElement.removeEventListener( Events.play, callback );
}
}
export class OnPause
{
static add( audioElement:HTMLAudioElement, callback:( e:Event )=>void, options:any = undefined )
{
audioElement.addEventListener( Events.pause, callback, options );
}
static remove( audioElement:HTMLAudioElement, callback:( e:Event )=>void )
{
audioElement.removeEventListener( Events.pause, callback );
}
}
export class OnEnded
{
static add( audioElement:HTMLAudioElement, callback:( e:Event )=>void, options:any = undefined )
{
audioElement.addEventListener( Events.ended, callback, options );
}
static remove( audioElement:HTMLAudioElement, callback:( e:Event )=>void )
{
audioElement.removeEventListener( Events.ended, callback );
}
}
export class OnTimeUpdate
{
static add( audioElement:HTMLAudioElement, callback:( e:Event )=>void, options:any = undefined )
{
audioElement.addEventListener( Events.timeupdate, callback, options );
}
static remove( audioElement:HTMLAudioElement, callback:( e:Event )=>void )
{
audioElement.removeEventListener( Events.timeupdate, callback );
}
}
export class OnLoad
{
static add( element:HTMLScriptElement|FileReader, callback:( e:Event )=>void, options:any = undefined )
{
element.addEventListener( Events.load, callback, options );
}
static remove( element:HTMLScriptElement|FileReader, callback:( e:Event )=>void )
{
element.removeEventListener( Events.load, callback );
}
}
export class OnSelectStart
{
static add( element:HTMLElement, callback:( e:Event )=>void, options:any = undefined )
{
element.addEventListener( Events.selectstart, callback, options );
}
static remove( element:HTMLElement, callback:( e:Event )=>void )
{
element.removeEventListener( Events.selectstart, callback );
}
}