| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import {assertNotReached} from 'chrome://resources/js/assert.m.js'; |
| import {isChromeOS, isMac} from 'chrome://resources/js/cr.m.js'; |
| |
| |
| export enum Key { |
| Comma = 188, |
| Del = 46, |
| Down = 40, |
| End = 35, |
| Escape = 27, |
| Home = 36, |
| Ins = 45, |
| Left = 37, |
| MediaNextTrack = 176, |
| MediaPlayPause = 179, |
| MediaPrevTrack = 177, |
| MediaStop = 178, |
| PageDown = 34, |
| PageUp = 33, |
| Period = 190, |
| Right = 39, |
| Space = 32, |
| Tab = 9, |
| Up = 38, |
| } |
| |
| /** |
| * Enum for whether we require modifiers of a keycode. |
| */ |
| enum ModifierPolicy { |
| NOT_ALLOWED = 0, |
| REQUIRED = 1 |
| } |
| |
| /** |
| * Gets the ModifierPolicy. Currently only "MediaNextTrack", "MediaPrevTrack", |
| * "MediaStop", "MediaPlayPause" are required to be used without any modifier. |
| */ |
| function getModifierPolicy(keyCode: number): ModifierPolicy { |
| switch (keyCode) { |
| case Key.MediaNextTrack: |
| case Key.MediaPlayPause: |
| case Key.MediaPrevTrack: |
| case Key.MediaStop: |
| return ModifierPolicy.NOT_ALLOWED; |
| default: |
| return ModifierPolicy.REQUIRED; |
| } |
| } |
| |
| /** |
| * Returns whether the keyboard event has a key modifier, which could affect |
| * how it's handled. |
| * @param countShiftAsModifier Whether the 'Shift' key should be counted as |
| * modifier. |
| * @return Whether the event has any modifiers. |
| */ |
| function hasModifier(e: KeyboardEvent, countShiftAsModifier: boolean): boolean { |
| return e.ctrlKey || e.altKey || |
| // Meta key is only relevant on Mac and CrOS, where we treat Command |
| // and Search (respectively) as modifiers. |
| (isMac && e.metaKey) || (isChromeOS && e.metaKey) || |
| (countShiftAsModifier && e.shiftKey); |
| } |
| |
| /** |
| * Checks whether the passed in |keyCode| is a valid extension command key. |
| * @return Whether the key is valid. |
| */ |
| export function isValidKeyCode(keyCode: number): boolean { |
| if (keyCode === Key.Escape) { |
| return false; |
| } |
| for (const k in Key) { |
| if (Key[k as keyof typeof Key] === keyCode) { |
| return true; |
| } |
| } |
| return (keyCode >= 'A'.charCodeAt(0) && keyCode <= 'Z'.charCodeAt(0)) || |
| (keyCode >= '0'.charCodeAt(0) && keyCode <= '9'.charCodeAt(0)); |
| } |
| |
| /** |
| * Converts a keystroke event to string form, ignoring invalid extension |
| * commands. |
| */ |
| export function keystrokeToString(e: KeyboardEvent): string { |
| const output = []; |
| // TODO(devlin): Should this be i18n'd? |
| if (isMac && e.metaKey) { |
| output.push('Command'); |
| } |
| if (isChromeOS && e.metaKey) { |
| output.push('Search'); |
| } |
| if (e.ctrlKey) { |
| output.push('Ctrl'); |
| } |
| if (!e.ctrlKey && e.altKey) { |
| output.push('Alt'); |
| } |
| if (e.shiftKey) { |
| output.push('Shift'); |
| } |
| |
| const keyCode = e.keyCode; |
| if (isValidKeyCode(keyCode)) { |
| if ((keyCode >= 'A'.charCodeAt(0) && keyCode <= 'Z'.charCodeAt(0)) || |
| (keyCode >= '0'.charCodeAt(0) && keyCode <= '9'.charCodeAt(0))) { |
| output.push(String.fromCharCode(keyCode)); |
| } else { |
| switch (keyCode) { |
| case Key.Comma: |
| output.push('Comma'); |
| break; |
| case Key.Del: |
| output.push('Delete'); |
| break; |
| case Key.Down: |
| output.push('Down'); |
| break; |
| case Key.End: |
| output.push('End'); |
| break; |
| case Key.Home: |
| output.push('Home'); |
| break; |
| case Key.Ins: |
| output.push('Insert'); |
| break; |
| case Key.Left: |
| output.push('Left'); |
| break; |
| case Key.MediaNextTrack: |
| output.push('MediaNextTrack'); |
| break; |
| case Key.MediaPlayPause: |
| output.push('MediaPlayPause'); |
| break; |
| case Key.MediaPrevTrack: |
| output.push('MediaPrevTrack'); |
| break; |
| case Key.MediaStop: |
| output.push('MediaStop'); |
| break; |
| case Key.PageDown: |
| output.push('PageDown'); |
| break; |
| case Key.PageUp: |
| output.push('PageUp'); |
| break; |
| case Key.Period: |
| output.push('Period'); |
| break; |
| case Key.Right: |
| output.push('Right'); |
| break; |
| case Key.Space: |
| output.push('Space'); |
| break; |
| case Key.Tab: |
| output.push('Tab'); |
| break; |
| case Key.Up: |
| output.push('Up'); |
| break; |
| } |
| } |
| } |
| |
| return output.join('+'); |
| } |
| |
| /** |
| * Returns true if the event has valid modifiers. |
| * @param e The keyboard event to consider. |
| * @return Wether the event is valid. |
| */ |
| export function hasValidModifiers(e: KeyboardEvent): boolean { |
| switch (getModifierPolicy(e.keyCode)) { |
| case ModifierPolicy.REQUIRED: |
| return hasModifier(e, false); |
| case ModifierPolicy.NOT_ALLOWED: |
| return !hasModifier(e, true); |
| } |
| assertNotReached(); |
| } |