2022-09-28 00:38:36 +02:00
import Debug from '../../conf/Debug' ;
import PlayerData from '../video-data/PlayerData' ;
import ExtensionMode from '../../../common/enums/ExtensionMode.enum' ;
import Logger from '../Logger' ;
import PageInfo from '../video-data/PageInfo' ;
import Settings from '../Settings' ;
import VideoData from '../video-data/VideoData' ;
import EventBus , { EventBusCommand } from '../EventBus' ;
2022-11-20 14:46:01 +01:00
import KbmBase from './KbmBase' ;
2018-11-14 23:32:37 +01:00
2020-04-13 15:20:29 +02:00
if ( process . env . CHANNEL !== 'stable' ) {
2022-09-28 00:38:36 +02:00
console . info ( "Loading KeyboardHandler" ) ;
2020-04-13 15:20:29 +02:00
}
2022-09-27 01:48:08 +02:00
/ * *
2022-09-27 22:15:01 +02:00
* Handles keypresses and mouse movement .
*
* EventBus commands :
* kbm - enable enables keyboard shortcuts and mouse panning
* kbm - disable disables keyboard shortcuts and mouse panning
* kbm - set - config sets configuration for this module .
2022-09-27 01:48:08 +02:00
* /
2022-11-20 14:46:01 +01:00
export class KeyboardHandler extends KbmBase {
2021-02-20 00:09:17 +01:00
logger : Logger ;
settings : Settings ;
2022-05-06 00:23:15 +02:00
eventBus : EventBus ;
2021-02-20 00:09:17 +01:00
2022-09-28 00:38:36 +02:00
playerElements : HTMLElement [ ] = [ ] ;
2022-09-27 01:48:08 +02:00
allowShortcuts : boolean = true ;
2021-02-20 00:09:17 +01:00
inputs : string [ ] = [ 'input' , 'select' , 'button' , 'textarea' ] ;
2021-03-06 02:53:58 +01:00
keyboardLocalDisabled : boolean = false ;
2021-02-20 00:09:17 +01:00
2022-09-27 01:48:08 +02:00
mouseMoveActions : any [ ] = [ ] ;
keypressActions : any [ ] = [ ] ;
eventBusCommands : { [ x : string ] : EventBusCommand } = {
'kbm-enable' : {
function : ( ) = > this . enable ( )
} ,
'kbm-disable' : {
function : ( ) = > this . disable ( )
} ,
'kbm-set-config' : {
function : ( data : { config : any , temporary? : boolean } ) = > this . setConfig ( data . config , data . temporary ) ,
} ,
2022-09-28 00:38:36 +02:00
'uw-enable' : {
function : ( ) = > this . load ( )
} ,
'uw-disable' : {
function : ( ) = > this . disable ( )
} ,
2022-09-27 01:48:08 +02:00
}
2022-05-06 00:23:15 +02:00
2022-09-27 01:48:08 +02:00
//#region lifecycle
constructor ( eventBus : EventBus , settings : Settings , logger : Logger ) {
2022-11-20 14:46:01 +01:00
super ( eventBus , settings , logger ) ;
2022-05-06 00:23:15 +02:00
this . init ( ) ;
2018-11-14 23:32:37 +01:00
}
init() {
2022-09-28 00:38:36 +02:00
this . logger . log ( 'info' , 'debug' , "[KeyboardHandler::init] starting init" ) ;
2018-11-18 18:44:44 +01:00
2022-05-06 00:23:15 +02:00
// build the action list — but only from actions that have shortcuts assigned
for ( const key in this . settings . active . commands ) {
for ( const command of this . settings . active . commands [ key ] ) {
if ( command . shortcut ) {
2022-09-27 01:48:08 +02:00
this . keypressActions . push ( command ) ;
2018-12-31 03:34:26 +01:00
}
2018-11-14 23:32:37 +01:00
}
}
2022-09-27 01:48:08 +02:00
this . load ( ) ;
}
load() {
2022-11-20 15:23:23 +01:00
if ( ! ( this . settings . isEnabledForSite ( ) && this . settings . active . kbm . enabled ) ) {
2022-09-28 00:38:36 +02:00
return ;
}
this . addListener ( ) ;
2022-09-27 01:48:08 +02:00
}
2022-05-06 00:23:15 +02:00
2022-09-27 01:48:08 +02:00
destroy() {
this . removeListener ( ) ;
}
// convenience methods
addListener() {
2021-11-25 20:00:32 +01:00
// events should be handled in handleEvent function. We need to do things this
2021-02-09 00:54:04 +01:00
// way, otherwise we can't remove event listener
2020-10-21 23:52:16 +02:00
// https://stackoverflow.com/a/19507086
2019-07-18 21:25:58 +02:00
2022-09-28 00:39:49 +02:00
if ( this . settings . active . kbm . keyboardEnabled ) {
2022-09-28 00:38:36 +02:00
document . addEventListener ( 'keyup' , this ) ;
}
2018-11-14 23:32:37 +01:00
}
2022-09-27 01:48:08 +02:00
removeListener() {
document . removeEventListener ( 'keyup' , this ) ;
}
2022-09-28 00:38:36 +02:00
/ * *
* Adds listeners for mouse events , for all player elements associated with the KeyboardHandler instance .
* @param element
* /
addMouseListeners ( element? : HTMLElement ) {
if ( element ) {
this . playerElements . push ( element ) ;
2022-09-27 01:48:08 +02:00
2022-09-28 00:39:49 +02:00
if ( this . settings . active . kbm . mouseEnabled ) {
2022-09-28 00:38:36 +02:00
element . addEventListener ( 'mousemove' , this ) ;
}
} else {
2022-09-28 00:39:49 +02:00
if ( this . settings . active . kbm . mouseEnabled ) {
2022-09-28 00:38:36 +02:00
for ( const playerElement of this . playerElements ) {
playerElement . addEventListener ( 'mousemove' , this ) ;
}
}
}
2022-09-27 01:48:08 +02:00
}
2022-09-28 00:38:36 +02:00
//#endregion
2018-11-14 23:32:37 +01:00
2022-09-28 00:38:36 +02:00
/ * *
* Current instance needs this method for document . addEventListener ( 'keyup' , this ) to work
* @param event
* /
2020-10-21 23:52:16 +02:00
handleEvent ( event ) {
switch ( event . type ) {
2021-11-25 20:00:32 +01:00
case 'keyup' :
2020-10-21 23:52:16 +02:00
this . handleKeyup ( event ) ;
break ;
}
}
2022-09-27 01:48:08 +02:00
/ * *
2022-09-28 00:38:36 +02:00
* Enables KeyboardHandler
* /
enable() {
2022-09-28 01:18:58 +02:00
this . load ( ) ;
2022-09-28 00:38:36 +02:00
}
/ * *
* Disables KeyboardHandler
* /
disable() {
this . removeListener ( ) ;
}
/ * *
* Sets configuration parameter for KeyboardHandler
2022-09-27 01:48:08 +02:00
* @param config
* /
setConfig ( config , temporary = false ) {
if ( temporary ) {
for ( const confKey in config ) {
switch ( confKey ) {
case 'enabled' :
config [ confKey ] ? this . enable ( ) : this . disable ( ) ;
break ;
}
}
return ;
}
for ( const confKey in config ) {
2022-09-28 00:39:49 +02:00
this . settings . active . kbm [ confKey ] = config [ confKey ] ;
2022-09-27 01:48:08 +02:00
}
this . settings . save ( ) ;
this . load ( ) ;
2020-10-21 23:52:16 +02:00
}
2019-06-02 23:54:32 +02:00
setKeyboardLocal ( state ) {
if ( state === ExtensionMode . Enabled ) {
this . keyboardLocalDisabled = false ;
} else if ( state === ExtensionMode . Disabled ) {
this . keyboardLocalDisabled = true ;
}
// don't do shit on invalid value of state
}
2019-10-27 23:37:13 +01:00
preventAction ( event ) {
2018-11-14 23:32:37 +01:00
var activeElement = document . activeElement ;
2020-01-30 01:06:02 +01:00
if ( this . logger . canLog ( 'keyboard' ) ) {
2019-07-18 21:25:58 +02:00
this . logger . pause ( ) ; // temp disable to avoid recursing;
2020-01-30 01:06:02 +01:00
const preventAction = this . preventAction ( event ) ;
2019-10-27 23:37:13 +01:00
this . logger . resume ( ) ; // undisable
2022-09-28 00:38:36 +02:00
this . logger . log ( 'info' , 'keyboard' , "[KeyboardHandler::preventAction] Testing whether we're in a textbox or something. Detailed rundown of conditions:\n" +
2018-11-15 00:18:41 +01:00
"\nis tag one of defined inputs? (yes->prevent):" , this . inputs . indexOf ( activeElement . tagName . toLocaleLowerCase ( ) ) !== - 1 ,
2018-11-14 23:32:37 +01:00
"\nis role = textbox? (yes -> prevent):" , activeElement . getAttribute ( "role" ) === "textbox" ,
"\nis type === 'text'? (yes -> prevent):" , activeElement . getAttribute ( "type" ) === "text" ,
2019-10-27 23:37:13 +01:00
"\nevent.target.isContentEditable? (yes -> prevent):" , event . target . isContentEditable ,
2019-06-02 23:54:32 +02:00
"\nis keyboard local disabled? (yes -> prevent):" , this . keyboardLocalDisabled ,
"\nis keyboard enabled in settings? (no -> prevent)" , this . settings . keyboardShortcutsEnabled ( window . location . hostname ) ,
2019-10-27 23:37:13 +01:00
"\nwill the action be prevented? (yes -> prevent)" , preventAction ,
2018-11-14 23:32:37 +01:00
"\n-----------------{ extra debug info }-------------------" ,
"\ntag name? (lowercase):" , activeElement . tagName , activeElement . tagName . toLocaleLowerCase ( ) ,
"\nrole:" , activeElement . getAttribute ( 'role' ) ,
"\ntype:" , activeElement . getAttribute ( 'type' ) ,
2019-10-27 23:37:13 +01:00
"\ninsta-fail inputs:" , this . inputs ,
"\nevent:" , event ,
"\nevent.target:" , event . target
2018-11-14 23:32:37 +01:00
) ;
}
2019-06-02 23:54:32 +02:00
if ( this . keyboardLocalDisabled ) {
return true ;
}
if ( ! this . settings . keyboardShortcutsEnabled ( window . location . hostname ) ) {
return true ;
}
2018-11-14 23:32:37 +01:00
if ( this . inputs . indexOf ( activeElement . tagName . toLocaleLowerCase ( ) ) !== - 1 ) {
return true ;
2021-11-25 20:00:32 +01:00
}
2018-11-14 23:32:37 +01:00
if ( activeElement . getAttribute ( "role" ) === "textbox" ) {
return true ;
}
2019-10-27 23:37:13 +01:00
if ( event . target . isContentEditable ) {
return true ;
}
2018-11-14 23:32:37 +01:00
if ( activeElement . getAttribute ( "type" ) === "text" ) {
return true ;
}
return false ;
}
2022-05-06 00:23:15 +02:00
ghettoLocalizeKeyboardEvent ( event : KeyboardEvent ) {
const realKey = event . key . toLocaleUpperCase ( ) ;
const isLatin = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' . indexOf ( realKey ) !== - 1 ;
if ( ! isLatin ) {
return event ;
}
const nativeKey = event . code . substring ( 3 ) ;
if ( nativeKey !== realKey ) {
return {
. . . event ,
code : ` Key ${ realKey } `
} ;
}
return event ;
}
2019-10-22 01:33:56 +02:00
isLatin ( key ) {
2022-05-06 00:23:15 +02:00
return 'abcdefghijklmnopqrstuvwxyz1234567890' . indexOf ( key . toLocaleLowerCase ( ) ) !== - 1 ;
2019-10-22 01:33:56 +02:00
}
isActionMatchStandard ( shortcut , event ) {
2018-11-14 23:32:37 +01:00
return shortcut . key === event . key &&
shortcut . ctrlKey === event . ctrlKey &&
shortcut . metaKey === event . metaKey &&
shortcut . altKey === event . altKey &&
shortcut . shiftKey === event . shiftKey
}
2019-10-23 19:34:58 +02:00
isActionMatchKeyCode ( shortcut , event ) {
return shortcut . code === event . code &&
2019-10-22 01:33:56 +02:00
shortcut . ctrlKey === event . ctrlKey &&
shortcut . metaKey === event . metaKey &&
shortcut . altKey === event . altKey &&
shortcut . shiftKey === event . shiftKey
}
isActionMatch ( shortcut , event , isLatin = true ) {
2019-10-23 19:34:58 +02:00
// ASCII and symbols fall back to key code matching, because we don't know for sure that
2019-10-22 01:33:56 +02:00
// regular matching by key is going to work
2021-11-25 20:00:32 +01:00
return isLatin ?
this . isActionMatchStandard ( shortcut , event ) :
2019-10-23 19:34:58 +02:00
this . isActionMatchStandard ( shortcut , event ) || this . isActionMatchKeyCode ( shortcut , event ) ;
2019-10-22 01:33:56 +02:00
}
2018-11-14 23:32:37 +01:00
2018-12-02 23:51:34 +01:00
2018-11-14 23:32:37 +01:00
handleKeyup ( event ) {
2022-09-28 01:18:58 +02:00
// if (!this.keyboardEnabled) {
// this.logger.log('info', 'keyboard', "%c[KeyboardHandler::handleKeyup] kbmHandler.keyboardEnabled is set to false. Doing nothing.");
// return;
// }
2022-09-28 00:38:36 +02:00
this . logger . log ( 'info' , 'keyboard' , "%c[KeyboardHandler::handleKeyup] we pressed a key: " , "color: #ff0" , event . key , " | keyup: " , event . keyup , "event:" , event ) ;
2018-11-14 23:32:37 +01:00
2021-08-24 02:13:05 +02:00
try {
if ( this . preventAction ( event ) ) {
2022-09-28 00:38:36 +02:00
this . logger . log ( 'info' , 'keyboard' , "[KeyboardHandler::handleKeyup] we are in a text box or something. Doing nothing." ) ;
2021-08-24 02:13:05 +02:00
return ;
}
2018-11-14 23:32:37 +01:00
2022-09-28 00:38:36 +02:00
this . logger . log ( 'info' , 'keyboard' , "%c[KeyboardHandler::handleKeyup] Trying to find and execute action for event. Actions/event: " , "color: #ff0" , this . keypressActions , event ) ;
2018-11-15 00:18:41 +01:00
2022-05-06 00:23:15 +02:00
const isLatin = this . isLatin ( event . key ) ;
2018-11-15 00:18:41 +01:00
2022-09-27 01:48:08 +02:00
for ( const command of this . keypressActions ) {
2022-05-06 00:23:15 +02:00
if ( this . isActionMatch ( command . shortcut , event , isLatin ) ) {
2022-07-31 01:12:54 +02:00
this . eventBus . send ( command . action , command . arguments ) ;
2022-05-06 00:23:15 +02:00
}
2021-08-24 02:13:05 +02:00
}
} catch ( e ) {
2022-09-28 00:38:36 +02:00
this . logger . log ( 'info' , 'debug' , '[KeyboardHandler::handleKeyup] Failed to handle keyup!' , e ) ;
2021-08-24 02:13:05 +02:00
}
2018-11-14 23:32:37 +01:00
}
2018-12-30 23:41:44 +01:00
}
2020-04-13 15:20:29 +02:00
if ( process . env . CHANNEL !== 'stable' ) {
2022-09-28 00:38:36 +02:00
console . info ( "KeyboardHandler loaded" ) ;
2020-04-13 15:20:29 +02:00
}
2022-09-28 00:38:36 +02:00
export default KeyboardHandler ;