blob: fb56f749021d67ea5731e6013efd06826f79627e [file] [log] [blame]
// 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();
}