/**
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ import { assert } from './assert.js' ; import { CDPSession } from './Connection.js' ; import { keyDefinitions , KeyDefinition , KeyInput } from './USKeyboardLayout.js' ; import { Protocol } from 'devtools-protocol' ; import { Point } from './JSHandle.js' ; type KeyDescription = Required < Pick < KeyDefinition , 'keyCode' | 'key' | 'text' | 'code' | 'location' > > ; /**
 * Keyboard provides an api for managing a virtual keyboard.
 * The high level api is  Keyboard . ,
 * which takes raw characters and generates proper keydown, keypress/input,
 * and keyup events on your page.
 *
 *  remarks For finer control, you can use  Keyboard . down ,
 *  Keyboard . up , and  Keyboard . sendCharacter * to manually fire events as if they were generated from a real keyboard.
 *
 * On MacOS, keyboard shortcuts like `⌘ A` -\> Select All do not work.
 * See  https .
 *
 *  example example public export class Keyboard { private _client : CDPSession ; internal _modifiers = 0 ; private _pressedKeys = new Set < string > ( ) ; internal constructor ( client ) { this . _client = client ; } remarks If `key` is a single character and no modifier keys besides `Shift`
     * are being held down, a `keypress`/`input` event will also generated.
     * The `text` option can be specified to force an input event to be generated.
     * If `key` is a modifier key, `Shift`, `Meta`, `Control`, or `Alt`,
     * subsequent key presses will be sent with that modifier active.
     * To release the modifier key, use  Keyboard . up .
     *
     * After the key is pressed once, subsequent calls to
     *  Keyboard . down will have
     *  https * set to true. To release the key, use  Keyboard . up .
     *
     * Modifier keys DO influence  Keyboard . down .
     * Holding down `Shift` will type the text in upper case.
     *
     *  param key - Name of key to press, such as `ArrowLeft`.
     * See  KeyInput for a list of all key names.
     *
     *  param options async down ( key , options = { text : undefined } ) { const description = this . _keyDescriptionForString ( key ) ; const autoRepeat = this . _pressedKeys . has ( description . code ) ; this . _pressedKeys . add ( description . code ) ; this . _modifiers |= this . _modifierBit ( description . key ) ; const text = options . text === undefined ? description . text : options . text ; await this . _client . send ( 'Input.dispatchKeyEvent' , { type : text ? 'keyDown' : 'rawKeyDown' , modifiers : this . _modifiers , windowsVirtualKeyCode : description . keyCode , code : description . code , key : description . key , text : text , unmodifiedText : text , autoRepeat , location : description . location , isKeypad : description . location === 3 , } ) ; } private _modifierBit ( key ) { if ( key === 'Alt' ) return 1 ; if ( key === 'Control' ) return 2 ; if ( key === 'Meta' ) return 4 ; if ( key === 'Shift' ) return 8 ; return 0 ; } private _keyDescriptionForString ( keyString ) { const shift = this . _modifiers & 8 ; const description = { key : '' , keyCode : 0 , code : '' , text : '' , location : 0 , } ; const definition = keyDefinitions [ keyString ] ; assert ( definition , `Unknown key: "${ keyString }"` ) ; if ( definition . key ) description . key = definition . key ; if ( shift && definition . shiftKey ) description . key = definition . shiftKey ; if ( definition . keyCode ) description . keyCode = definition . keyCode ; if ( shift && definition . shiftKeyCode ) description . keyCode = definition . shiftKeyCode ; if ( definition . code ) description . code = definition . code ; if ( definition . location ) description . location = definition . location ; if ( description . key . length === 1 ) description . text = description . key ; if ( definition . text ) description . text = definition . text ; if ( shift && definition . shiftText ) description . text = definition . shiftText ; if ( this . _modifiers & ~ 8 ) description . text = '' ; return description ; } param key - Name of key to release, such as `ArrowLeft`.
     * See  KeyInput * for a list of all key names.
      async up ( key ) { const description = this . _keyDescriptionForString ( key ) ; this . _modifiers &= ~ this . _modifierBit ( description . key ) ; this . _pressedKeys . delete ( description . code ) ; await this . _client . send ( 'Input.dispatchKeyEvent' , { type : 'keyUp' , modifiers : this . _modifiers , key : description . key , windowsVirtualKeyCode : description . keyCode , code : description . code , location : description . location , } ) ; } remarks Modifier keys DO NOT effect  Keyboard . sendCharacter .
     * Holding down `Shift` will not type the text in upper case.
     *
     *  example param char async sendCharacter ( char ) { await this . _client . send ( 'Input.insertText' , { text : char } ) ; } private charIsKey ( char ) { return ! ! keyDefinitions [ char ] ; } remarks To press a special key, like `Control` or `ArrowDown`,
     * use  Keyboard . press .
     *
     * Modifier keys DO NOT effect `keyboard.type`.
     * Holding down `Shift` will not type the text in upper case.
     *
     *  example param text param options async type ( text , options = { } ) { const delay = options . delay || null ; for ( const char of text ) { if ( this . charIsKey ( char ) ) { await this . press ( char , { delay } ) ; } else { if ( delay ) await new Promise ( ( f ) => setTimeout ( f , delay ) ) ; await this . sendCharacter ( char ) ; } } } /**
     * Shortcut for  Keyboard . down * and  Keyboard . up .
     *
     *  remarks If `key` is a single character and no modifier keys besides `Shift`
     * are being held down, a `keypress`/`input` event will also generated.
     * The `text` option can be specified to force an input event to be generated.
     *
     * Modifier keys DO effect  Keyboard . press .
     * Holding down `Shift` will type the text in upper case.
     *
     *  param key - Name of key to press, such as `ArrowLeft`.
     * See  KeyInput for a list of all key names.
     *
     *  param options async press ( key , options = { } ) { const { delay = null } = options ; await this . down ( key , options ) ; if ( delay ) await new Promise ( ( f ) => setTimeout ( f , options . delay ) ) ; await this . up ( key ) ; } } public export type MouseButton = 'left' | 'right' | 'middle' ; public export interface MouseOptions { button ? : MouseButton ; clickCount ? : number ; } public export interface MouseWheelOptions { deltaX ? : number ; deltaY ? : number ; } remarks example ```js
 * // Using ‘page.mouse’ to trace a 100x100 square.
 * await page.mouse.move(0, 0);
 * await page.mouse.down();
 * await page.mouse.move(0, 100);
 * await page.mouse.move(100, 100);
 * await page.mouse.move(100, 0);
 * await page.mouse.move(0, 0);
 * await page.mouse.up();
 * ```
 *
 * **Note**: The mouse events trigger synthetic `MouseEvent`s.
 * This means that it does not fully replicate the functionality of what a normal user
 * would be able to do with their mouse.
 *
 * For example, dragging and selecting text is not possible using `page.mouse`.
 * Instead, you can use the  https functionality implemented in the platform.
 *
 *  example public export class Mouse { private _client : CDPSession ; private _keyboard : Keyboard ; private _x = 0 ; private _y = 0 ; private _button : MouseButton | 'none' = 'none' ; internal constructor ( client , keyboard ) { this . _client = client ; this . _keyboard = keyboard ; } param x param y param options async move ( x , y , options = { } ) { const { steps = 1 } = options ; const fromX = this . _x , fromY = this . _y ; this . _x = x ; this . _y = y ; for ( let i = 1 ; i <= steps ; i ++ ) { await this . _client . send ( 'Input.dispatchMouseEvent' , { type : 'mouseMoved' , button : this . _button , x : fromX + ( this . _x - fromX ) * ( i / steps ) , y : fromY + ( this . _y - fromY ) * ( i / steps ) , modifiers : this . _keyboard . _modifiers , } ) ; } } param x param y param options async click ( x , y , options = { } ) { const { delay = null } = options ; if ( delay !== null ) { await this . move ( x , y ) ; await this . down ( options ) ; await new Promise ( ( f ) => setTimeout ( f , delay ) ) ; await this . up ( options ) ; } else { await this . move ( x , y ) ; await this . down ( options ) ; await this . up ( options ) ; } } param options async down ( options = { } ) { const { button = 'left' , clickCount = 1 } = options ; this . _button = button ; await this . _client . send ( 'Input.dispatchMouseEvent' , { type : 'mousePressed' , button , x : this . _x , y : this . _y , modifiers : this . _keyboard . _modifiers , clickCount , } ) ; } param options async up ( options = { } ) { const { button = 'left' , clickCount = 1 } = options ; this . _button = 'none' ; await this . _client . send ( 'Input.dispatchMouseEvent' , { type : 'mouseReleased' , button , x : this . _x , y : this . _y , modifiers : this . _keyboard . _modifiers , clickCount , } ) ; } param options example async wheel ( options = { } ) { const { deltaX = 0 , deltaY = 0 } = options ; await this . _client . send ( 'Input.dispatchMouseEvent' , { type : 'mouseWheel' , x : this . _x , y : this . _y , deltaX , deltaY , modifiers : this . _keyboard . _modifiers , pointerType : 'mouse' , } ) ; } param start param target async drag ( start , target ) { const promise = new Promise < Protocol . Input . DragData > ( ( resolve ) => { this . _client . once ( 'Input.dragIntercepted' , ( event ) => resolve ( event . data ) ) ; } ) ; await this . move ( start . x , start . y ) ; await this . down ( ) ; await this . move ( target . x , target . y ) ; return promise ; } param target param data async dragEnter ( target , data ) { await this . _client . send ( 'Input.dispatchDragEvent' , { type : 'dragEnter' , x : target . x , y : target . y , modifiers : this . _keyboard . _modifiers , data , } ) ; } param target param data async dragOver ( target , data ) { await this . _client . send ( 'Input.dispatchDragEvent' , { type : 'dragOver' , x : target . x , y : target . y , modifiers : this . _keyboard . _modifiers , data , } ) ; } param target param data async drop ( target , data ) { await this . _client . send ( 'Input.dispatchDragEvent' , { type : 'drop' , x : target . x , y : target . y , modifiers : this . _keyboard . _modifiers , data , } ) ; } param target param target param options async dragAndDrop ( start , target , options = { } ) { const { delay = null } = options ; const data = await this . drag ( start , target ) ; await this . dragEnter ( target , data ) ; await this . dragOver ( target , data ) ; if ( delay ) { await new Promise ( ( resolve ) => setTimeout ( resolve , delay ) ) ; } await this . drop ( target , data ) ; await this . up ( ) ; } } public export class Touchscreen { private _client : CDPSession ; private _keyboard : Keyboard ; internal constructor ( client , keyboard ) { this . _client = client ; this . _keyboard = keyboard ; } param x param y async tap ( x , y ) { const touchPoints = [ { x : Math . round ( x ) , y : Math . round ( y ) } ] ; await this . _client . send ( 'Input.dispatchTouchEvent' , { type : 'touchStart' , touchPoints , modifiers : this . _keyboard . _modifiers , } ) ; await this . _client . send ( 'Input.dispatchTouchEvent' , { type : 'touchEnd' , touchPoints : [ ] , modifiers : this . _keyboard . _modifiers , } ) ; } }