import { osPlatform } from '../common/capabilities' ; import { getCommandTarget , SelectorPrivate } from '../editor/commands' ; import { DEFAULT_KEYBOARD_TOGGLE_GLYPH } from '../editor/options' ; import { VirtualKeyboard } from '../editor/virtual-keyboard-utils' ; import { Selector } from '../public/commands' ; import { Mathfield , VirtualKeyboardInterface } from '../public/mathfield' ; import { CoreOptions , OriginValidator , RemoteVirtualKeyboardOptions , VirtualKeyboardOptions , } from '../public/options' ; import { validateOrigin } from './utils' ; const POST_MESSAGE_TYPE = 'ml#systemPostMessage' ; interface RemoteKeyboardMessageData { type : 'ml#systemPostMessage' ; action : 'executeCommand' | 'focus' | 'blur' | 'updateState' ; state ? : { visible : boolean ; height : number ; } ; command ? : Selector | [ Selector , ... any [ ] ] ; } /**
 * Must be used on frame with mathfield editor
 */ export class VirtualKeyboardDelegate implements VirtualKeyboardInterface { visible : boolean ; height : number ; private readonly targetOrigin : string ; private readonly originValidator : OriginValidator ; private enabled : boolean ; private readonly _mathfield : Mathfield ; param targetOrigin constructor ( options ) { this . targetOrigin = options . targetOrigin ?? window . origin ; this . originValidator = options . originValidator ?? 'same-origin' ; this . _mathfield = options . mathfield ; this . enable ( ) ; } setOptions ( _options ) { } public create ( ) { } public dispose ( ) { this . disable ( ) ; } public enable ( ) { if ( ! this . enabled ) { this . enabled = true ; window . addEventListener ( 'message' , this ) ; } } public disable ( ) { if ( this . enabled ) { window . removeEventListener ( 'message' , this ) ; this . enabled = false ; } } public executeCommand ( command ) { if ( getCommandTarget ( command ) === 'virtual-keyboard' ) { this . sendMessage ( 'executeCommand' , { command } ) ; return false ; } return this . _mathfield ?. executeCommand ( command as Selector | [ Selector , ... any [ ] ] ) ; } focusMathfield ( ) { } blurMathfield ( ) { } stateChanged ( ) { } handleEvent ( event ) { if ( event . type === 'message' && event . data && event . data . type === POST_MESSAGE_TYPE ) { if ( ! validateOrigin ( event . origin , this . originValidator ) ) { throw new Error ( `Message from unknown origin (${ event . origin }) cannot be handled` ) ; } const { action } = event . data ; if ( action === 'executeCommand' ) { this . executeCommand ( event . data . command ! ) ; } else if ( action === 'updateState' ) { this . visible = event . data . state ! . visible ; this . height = event . data . state ! . height ; } else if ( action === 'focus' ) { this . _mathfield ?. focus ?. ( ) ; } else if ( action === 'blur' ) { this . _mathfield ?. blur ?. ( ) ; } } } private sendMessage ( action , payload = { } ) { if ( window . parent ) { window . parent . postMessage ( { type : POST_MESSAGE_TYPE , action , ... payload , } , this . targetOrigin ) ; return true ; } return false ; } } /**
 * Must be used on parent frame where virtual keyboard will be rendered
 */ export class RemoteVirtualKeyboard extends VirtualKeyboard { options : RemoteVirtualKeyboardOptions ; private sourceFrame : Window ; private readonly canUndoState : boolean ; private readonly canRedoState : boolean ; constructor ( options ) { super ( { ... RemoteVirtualKeyboard . defaultOptions , ... ( options ?? { } ) , } ) ; window . addEventListener ( 'message' , this ) ; } static get defaultOptions ( ) : RemoteVirtualKeyboardOptions { return { createHTML : ( s ) : any => s , fontsDirectory : './fonts' , soundsDirectory : './sounds' , targetOrigin : window . origin , originValidator : 'same-origin' , virtualKeyboards : 'all' , virtualKeyboardLayout : 'auto' , customVirtualKeyboardLayers : { } , customVirtualKeyboards : { } , virtualKeyboardTheme : /macos|ios/ . test ( osPlatform ( ) ) ? 'apple' : 'material' , keypressVibration : true , keypressSound : null , plonkSound : null , virtualKeyboardToolbar : 'default' , virtualKeyboardToggleGlyph : DEFAULT_KEYBOARD_TOGGLE_GLYPH , virtualKeyboardMode : 'auto' , virtualKeyboardContainer : globalThis . document ?. body ?? null , } ; } handleEvent ( event ) { if ( event . type === 'message' && event . data && event . data . type === POST_MESSAGE_TYPE ) { if ( ! validateOrigin ( event . origin , this . options . originValidator ) ) { throw new Error ( `Can not handle message from unknown origin (${ event . origin }).` ) ; } const { action } = event . data ; if ( action === 'executeCommand' ) { const { command } = event . data ; this . sourceFrame = event . source as Window ; this . executeCommand ( command ! ) ; } } } stateChanged ( ) { this . sendMessage ( 'stateChanged' , { state : { visible : this . visible , height : this . element ?. offsetHeight ?? 0 , } , } ) ; } public executeCommand ( command ) { const commandTarget = getCommandTarget ( command ) ; if ( commandTarget === 'virtual-keyboard' ) { return super . executeCommand ( command ) ; } this . sendMessage ( 'executeCommand' , { command } ) ; return false ; } category public focus ( ) { this . sendMessage ( 'focus' ) ; } category public blur ( ) { this . sendMessage ( 'blur' ) ; } public canUndo ( ) { return this . canUndoState ; } public canRedo ( ) { return this . canRedoState ; } public dispose ( ) { window . removeEventListener ( 'message' , this ) ; } private sendMessage ( action , payload = { } ) { this . sourceFrame ?. postMessage ( { type : POST_MESSAGE_TYPE , action , ... payload , } , this . options . targetOrigin ) ; } }