123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- /* global APP, $, interfaceConfig */
-
- import { toggleDialog } from '../../react/features/base/dialog';
- import { sendEvent } from '../../react/features/analytics';
- import { SpeakerStats } from '../../react/features/speaker-stats';
-
- const logger = require('jitsi-meet-logger').getLogger(__filename);
-
- /**
- * The reference to the shortcut dialogs when opened.
- */
- let keyboardShortcutDialog = null;
-
- /**
- * Shows or hides the keyboard shortcuts dialog.
- * @param {boolean} show whether to show or hide the dialog
- */
- function showKeyboardShortcutsPanel(show) {
- if (show
- && !APP.UI.messageHandler.isDialogOpened()
- && keyboardShortcutDialog === null) {
- const msg = $('#keyboard-shortcuts').html();
- const buttons = { Close: true };
-
- keyboardShortcutDialog = APP.UI.messageHandler.openDialog(
- 'keyboardShortcuts.keyboardShortcuts', msg, true, buttons);
- } else if (keyboardShortcutDialog !== null) {
- keyboardShortcutDialog.close();
- keyboardShortcutDialog = null;
- }
- }
-
- /**
- * Map of shortcuts. When a shortcut is registered it enters the mapping.
- * @type {{}}
- */
- const _shortcuts = {};
-
- /**
- * True if the keyboard shortcuts are enabled and false if not.
- * @type {boolean}
- */
- let enabled = true;
-
- /**
- * Maps keycode to character, id of popover for given function and function.
- */
- const KeyboardShortcut = {
- init() {
- this._initGlobalShortcuts();
-
- window.onkeyup = e => {
- if (!enabled) {
- return;
- }
- const key = this._getKeyboardKey(e).toUpperCase();
- const num = parseInt(key, 10);
-
- if (!($(':focus').is('input[type=text]')
- || $(':focus').is('input[type=password]')
- || $(':focus').is('textarea'))) {
- if (_shortcuts.hasOwnProperty(key)) {
- _shortcuts[key].function(e);
- } else if (!isNaN(num) && num >= 0 && num <= 9) {
- APP.UI.clickOnVideo(num);
- }
-
- // esc while the smileys are visible hides them
- } else if (key === 'ESCAPE'
- && $('#smileysContainer').is(':visible')) {
- APP.UI.toggleSmileys();
- }
- };
-
- window.onkeydown = e => {
- if (!enabled) {
- return;
- }
- if (!($(':focus').is('input[type=text]')
- || $(':focus').is('input[type=password]')
- || $(':focus').is('textarea'))) {
- if (this._getKeyboardKey(e).toUpperCase() === ' ') {
- if (APP.conference.isLocalAudioMuted()) {
- sendEvent('shortcut.talk.released');
- logger.log('Talk shortcut released');
- APP.conference.muteAudio(false);
- }
- }
- }
- };
- },
-
- /**
- * Enables/Disables the keyboard shortcuts.
- * @param {boolean} value - the new value.
- */
- enable(value) {
- enabled = value;
- },
-
- /**
- * Registers a new shortcut.
- *
- * @param shortcutChar the shortcut character triggering the action
- * @param shortcutAttr the "shortcut" html element attribute mappring an
- * element to this shortcut and used to show the shortcut character on the
- * element tooltip
- * @param exec the function to be executed when the shortcut is pressed
- * @param helpDescription the description of the shortcut that would appear
- * in the help menu
- */
- registerShortcut(// eslint-disable-line max-params
- shortcutChar,
- shortcutAttr,
- exec,
- helpDescription) {
- _shortcuts[shortcutChar] = {
- character: shortcutChar,
- shortcutAttr,
- function: exec
- };
-
- if (helpDescription) {
- this._addShortcutToHelp(shortcutChar, helpDescription);
- }
- },
-
- /**
- * Unregisters a shortcut.
- *
- * @param shortcutChar unregisters the given shortcut, which means it will
- * no longer be usable
- */
- unregisterShortcut(shortcutChar) {
- _shortcuts.remove(shortcutChar);
-
- this._removeShortcutFromHelp(shortcutChar);
- },
-
- /**
- * Returns the tooltip string for the given shortcut attribute.
- *
- * @param shortcutAttr indicates the popover associated with the shortcut
- * @returns {string} the tooltip string to add to the given shortcut popover
- * or an empty string if the shortcutAttr is null, an empty string or not
- * found in the shortcut mapping
- */
- getShortcutTooltip(shortcutAttr) {
- if (typeof shortcutAttr === 'string' && shortcutAttr.length > 0) {
- for (const key in _shortcuts) {
- if (_shortcuts.hasOwnProperty(key)
- && _shortcuts[key].shortcutAttr
- && _shortcuts[key].shortcutAttr === shortcutAttr) {
- return ` (${_shortcuts[key].character})`;
- }
- }
- }
-
- return '';
- },
-
- /**
- * @param e a KeyboardEvent
- * @returns {string} e.key or something close if not supported
- */
- _getKeyboardKey(e) {
- if (typeof e.key === 'string') {
- return e.key;
- }
- if (e.type === 'keypress'
- && ((e.which >= 32 && e.which <= 126)
- || (e.which >= 160 && e.which <= 255))) {
- return String.fromCharCode(e.which);
- }
-
- // try to fallback (0-9A-Za-z and QWERTY keyboard)
- switch (e.which) {
- case 27:
- return 'Escape';
- case 191:
- return e.shiftKey ? '?' : '/';
- }
- if (e.shiftKey || e.type === 'keypress') {
- return String.fromCharCode(e.which);
- }
-
- return String.fromCharCode(e.which).toLowerCase();
-
- },
-
- /**
- * Adds the given shortcut to the help dialog.
- *
- * @param shortcutChar the shortcut character
- * @param shortcutDescriptionKey the description of the shortcut
- * @private
- */
- _addShortcutToHelp(shortcutChar, shortcutDescriptionKey) {
-
- const listElement = document.createElement('li');
- const itemClass = 'shortcuts-list__item';
-
- listElement.className = itemClass;
- listElement.id = shortcutChar;
-
- const spanElement = document.createElement('span');
-
- spanElement.className = 'item-action';
-
- const kbdElement = document.createElement('kbd');
- const classes = 'aui-label regular-key';
-
- kbdElement.className = classes;
- kbdElement.innerHTML = shortcutChar;
- spanElement.appendChild(kbdElement);
-
- const descriptionElement = document.createElement('span');
- const descriptionClass = 'shortcuts-list__description';
-
- descriptionElement.className = descriptionClass;
- descriptionElement.setAttribute('data-i18n', shortcutDescriptionKey);
- APP.translation.translateElement($(descriptionElement));
-
- listElement.appendChild(spanElement);
- listElement.appendChild(descriptionElement);
-
- const parentListElement
- = document.getElementById('keyboard-shortcuts-list');
-
- if (parentListElement) {
- parentListElement.appendChild(listElement);
- }
- },
-
- /**
- * Removes the list element corresponding to the given shortcut from the
- * help dialog
- * @private
- */
- _removeShortcutFromHelp(shortcutChar) {
- const parentListElement
- = document.getElementById('keyboard-shortcuts-list');
-
- const shortcutElement = document.getElementById(shortcutChar);
-
- if (shortcutElement) {
- parentListElement.removeChild(shortcutElement);
- }
- },
-
- /**
- * Initialise global shortcuts.
- * Global shortcuts are shortcuts for features that don't have a button or
- * link associated with the action. In other words they represent actions
- * triggered _only_ with a shortcut.
- */
- _initGlobalShortcuts() {
- this.registerShortcut('ESCAPE', null, () => {
- showKeyboardShortcutsPanel(false);
- });
-
- this.registerShortcut('?', null, () => {
- sendEvent('shortcut.shortcut.help');
- showKeyboardShortcutsPanel(true);
- }, 'keyboardShortcuts.toggleShortcuts');
-
- // register SPACE shortcut in two steps to insure visibility of help
- // message
- this.registerShortcut(' ', null, () => {
- sendEvent('shortcut.talk.clicked');
- logger.log('Talk shortcut pressed');
- APP.conference.muteAudio(true);
- });
- this._addShortcutToHelp('SPACE', 'keyboardShortcuts.pushToTalk');
-
- if (!interfaceConfig.filmStripOnly) {
- this.registerShortcut('T', null, () => {
- sendEvent('shortcut.speakerStats.clicked');
- APP.store.dispatch(toggleDialog(SpeakerStats, {
- conference: APP.conference
- }));
- }, 'keyboardShortcuts.showSpeakerStats');
- }
-
- /**
- * FIXME: Currently focus keys are directly implemented below in
- * onkeyup. They should be moved to the SmallVideo instead.
- */
- this._addShortcutToHelp('0', 'keyboardShortcuts.focusLocal');
- this._addShortcutToHelp('1-9', 'keyboardShortcuts.focusRemote');
- }
- };
-
- export default KeyboardShortcut;
|