Attach keyboard shortcuts to features

This commit is contained in:
yanas 2016-08-28 22:59:09 -05:00
parent 84834dc4e6
commit 7076ada6f4
6 changed files with 221 additions and 160 deletions

View File

@ -16,4 +16,9 @@
#keyboard-shortcuts .item-action { #keyboard-shortcuts .item-action {
color: #209EFF; color: #209EFF;
font-size: 14pt; font-size: 14pt;
padding-right: 5px;
}
#keyboard-shortcuts-list {
list-style-type: none;
} }

View File

@ -279,67 +279,7 @@
<div id="keyboard-shortcuts" class="keyboard-shortcuts" style="display:none;"> <div id="keyboard-shortcuts" class="keyboard-shortcuts" style="display:none;">
<div class="header"><h3 data-i18n="keyboardShortcuts.keyboardShortcuts"></h3></div> <div class="header"><h3 data-i18n="keyboardShortcuts.keyboardShortcuts"></h3></div>
<div class="content"> <div class="content">
<ul class="item"> <ul id="keyboard-shortcuts-list" class="item">
<li>
<span class="item-action">
<kbd class="regular-key">M</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.mute"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">V</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.videoMute"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">C</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.toggleChat"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">R</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.raiseHand"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">T</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.pushToTalk"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">D</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.toggleScreensharing"></span>
</li>
<li class="item-details">
<span class="item-action">
<kbd class="regular-key">F</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.toggleFilmstrip"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">?</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.toggleShortcuts"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">0</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.focusLocal"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">1-9</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.focusRemote"></span>
</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -3,9 +3,15 @@ import UIUtil from '../util/UIUtil';
import UIEvents from '../../../service/UI/UIEvents'; import UIEvents from '../../../service/UI/UIEvents';
const defaultBottomToolbarButtons = { const defaultBottomToolbarButtons = {
'chat': '#bottom_toolbar_chat', 'chat': {
'contacts': '#bottom_toolbar_contact_list', id: '#bottom_toolbar_chat'
'filmstrip': '#bottom_toolbar_film_strip' },
'contacts': {
id: '#bottom_toolbar_contact_list'
},
'filmstrip': {
id: '#bottom_toolbar_film_strip'
}
}; };
const BottomToolbar = { const BottomToolbar = {

View File

@ -2,6 +2,7 @@
/* jshint -W101 */ /* jshint -W101 */
import UIUtil from '../util/UIUtil'; import UIUtil from '../util/UIUtil';
import UIEvents from '../../../service/UI/UIEvents'; import UIEvents from '../../../service/UI/UIEvents';
import KeyboardShortcut from '../../keyboardshortcut/KeyboardShortcut';
let roomUrl = null; let roomUrl = null;
let emitter = null; let emitter = null;
@ -110,7 +111,8 @@ const buttonHandlers = {
}, },
"toolbar_button_fullScreen": function() { "toolbar_button_fullScreen": function() {
JitsiMeetJS.analytics.sendEvent('toolbar.fullscreen.enabled'); JitsiMeetJS.analytics.sendEvent('toolbar.fullscreen.enabled');
UIUtil.buttonClick("#toolbar_button_fullScreen", "icon-full-screen icon-exit-full-screen"); UIUtil.buttonClick("#toolbar_button_fullScreen",
"icon-full-screen icon-exit-full-screen");
emitter.emit(UIEvents.FULLSCREEN_TOGGLE); emitter.emit(UIEvents.FULLSCREEN_TOGGLE);
}, },
"toolbar_button_sip": function () { "toolbar_button_sip": function () {
@ -152,16 +154,64 @@ const buttonHandlers = {
} }
}; };
const defaultToolbarButtons = { const defaultToolbarButtons = {
'microphone': '#toolbar_button_mute', 'microphone': {
'camera': '#toolbar_button_camera', id: '#toolbar_button_mute',
'desktop': '#toolbar_button_desktopsharing', shortcut: 'M',
'security': '#toolbar_button_security', shortcutAttr: 'mutePopover',
'invite': '#toolbar_button_link', shortcutFunc: function() {
'chat': '#toolbar_button_chat', JitsiMeetJS.analytics.sendEvent('shortcut.audiomute.toggled');
'etherpad': '#toolbar_button_etherpad', APP.conference.toggleAudioMuted();
'fullscreen': '#toolbar_button_fullScreen', },
'settings': '#toolbar_button_settings', shortcutDescription: "keyboardShortcuts.mute"
'hangup': '#toolbar_button_hangup' },
'camera': {
id: '#toolbar_button_camera',
shortcut: 'V',
shortcutAttr: 'toggleVideoPopover',
shortcutFunc: function() {
JitsiMeetJS.analytics.sendEvent('shortcut.videomute.toggled');
APP.conference.toggleVideoMuted();
},
shortcutDescription: "keyboardShortcuts.videoMute"
},
'desktop': {
id: '#toolbar_button_desktopsharing',
shortcut: 'D',
shortcutAttr: 'toggleDesktopSharingPopover',
shortcutFunc: function() {
JitsiMeetJS.analytics.sendEvent('shortcut.screen.toggled');
APP.conference.toggleScreenSharing();
},
shortcutDescription: "keyboardShortcuts.toggleScreensharing"
},
'security': {
id: '#toolbar_button_security'
},
'invite': {
id: '#toolbar_button_link'
},
'chat': {
id: '#toolbar_button_chat',
shortcut: 'C',
shortcutAttr: 'toggleChatPopover',
shortcutFunc: function() {
JitsiMeetJS.analytics.sendEvent('shortcut.chat.toggled');
APP.UI.toggleChat();
},
shortcutDescription: "keyboardShortcuts.toggleChat"
},
'etherpad': {
id: '#toolbar_button_etherpad'
},
'fullscreen': {
id: '#toolbar_button_fullScreen'
},
'settings': {
id: '#toolbar_button_settings'
},
'hangup': {
id: '#toolbar_button_hangup'
}
}; };
function dialpadButtonClicked() { function dialpadButtonClicked() {
@ -197,6 +247,22 @@ const Toolbar = {
UIUtil.hideDisabledButtons(defaultToolbarButtons); UIUtil.hideDisabledButtons(defaultToolbarButtons);
Object.keys(defaultToolbarButtons).forEach(
id => {
if (UIUtil.isButtonEnabled(id)) {
var button = defaultToolbarButtons[id];
if (button.shortcut)
KeyboardShortcut.registerShortcut(
button.shortcut,
button.shortcutAttr,
button.shortcutFunc,
button.shortcutDescription
);
}
}
);
Object.keys(buttonHandlers).forEach( Object.keys(buttonHandlers).forEach(
buttonId => $(`#${buttonId}`).click(function(event) { buttonId => $(`#${buttonId}`).click(function(event) {
!$(this).prop('disabled') && buttonHandlers[buttonId](event); !$(this).prop('disabled') && buttonHandlers[buttonId](event);

View File

@ -130,7 +130,7 @@
var selector = Object.keys(mappings) var selector = Object.keys(mappings)
.map(function (buttonName) { .map(function (buttonName) {
return UIUtil.isButtonEnabled(buttonName) return UIUtil.isButtonEnabled(buttonName)
? null : mappings[buttonName]; }) ? null : mappings[buttonName].id; })
.filter(function (item) { return item; }) .filter(function (item) { return item; })
.join(','); .join(',');
$(selector).hide(); $(selector).hide();

View File

@ -1,91 +1,64 @@
/* global APP, $, JitsiMeetJS */ /* global APP, $, JitsiMeetJS */
//maps keycode to character, id of popover for given function and function
var shortcuts = {};
function initShortcutHandlers() {
shortcuts = {
"ESCAPE": {
character: "Esc",
function: function() {
APP.UI.showKeyboardShortcutsPanel(false);
}
},
"C": {
character: "C",
id: "toggleChatPopover",
function: function() {
JitsiMeetJS.analytics.sendEvent('shortcut.chat.toggled');
APP.UI.toggleChat();
}
},
"D": {
character: "D",
id: "toggleDesktopSharingPopover",
function: function () {
JitsiMeetJS.analytics.sendEvent('shortcut.screen.toggled');
APP.conference.toggleScreenSharing();
}
},
"F": {
character: "F",
id: "filmstripPopover",
function: function() {
JitsiMeetJS.analytics.sendEvent('shortcut.film.toggled');
APP.UI.toggleFilmStrip();
}
},
"M": {
character: "M",
id: "mutePopover",
function: function() {
JitsiMeetJS.analytics.sendEvent('shortcut.audiomute.toggled');
APP.conference.toggleAudioMuted();
}
},
"R": {
character: "R",
function: function() {
JitsiMeetJS.analytics.sendEvent('shortcut.raisedhand.toggled');
APP.conference.maybeToggleRaisedHand();
}
}, /**
"T": { * Initialise global shortcuts.
character: "T", * Global shortcuts are shortcuts for features that don't have a button or
function: function() { * link associated with the action. In other words they represent actions
JitsiMeetJS.analytics.sendEvent('shortcut.talk.clicked'); * triggered _only_ with a shortcut.
APP.conference.muteAudio(true); */
} function initGlobalShortcuts() {
},
"V": { KeyboardShortcut.registerShortcut("ESCAPE", null, function() {
character: "V", APP.UI.showKeyboardShortcutsPanel(false);
id: "toggleVideoPopover", });
function: function() {
JitsiMeetJS.analytics.sendEvent('shortcut.videomute.toggled'); KeyboardShortcut.registerShortcut("?", null, function() {
APP.conference.toggleVideoMuted(); JitsiMeetJS.analytics.sendEvent("shortcut.shortcut.help");
} APP.UI.toggleKeyboardShortcutsPanel();
}, }, "keyboardShortcuts.toggleShortcuts");
"?": {
character: "?", KeyboardShortcut.registerShortcut("R", null, function() {
function: function(e) { JitsiMeetJS.analytics.sendEvent("shortcut.raisedhand.toggled");
JitsiMeetJS.analytics.sendEvent('shortcut.shortcut.help'); APP.conference.maybeToggleRaisedHand();
APP.UI.toggleKeyboardShortcutsPanel(); }, "keyboardShortcuts.raiseHand");
}
} KeyboardShortcut.registerShortcut("T", null, function() {
}; JitsiMeetJS.analytics.sendEvent("shortcut.talk.clicked");
APP.conference.muteAudio(true);
}, "keyboardShortcuts.pushToTalk");
KeyboardShortcut.registerShortcut("F", 'filmstripPopover', function() {
JitsiMeetJS.analytics.sendEvent("shortcut.film.toggled");
APP.UI.toggleFilmStrip();
}, "keyboardShortcuts.toggleFilmstrip");
// Focus keys are directly implemented below.
KeyboardShortcut._addShortcutToHelp("0", "keyboardShortcuts.focusLocal");
KeyboardShortcut._addShortcutToHelp("1-9", "keyboardShortcuts.focusRemote");
} }
/**
* Map of shortcuts. When a shortcut is registered it enters the mapping.
* @type {{}}
*/
let _shortcuts = {};
/**
* Maps keycode to character, id of popover for given function and function.
*/
var KeyboardShortcut = { var KeyboardShortcut = {
init: function () { init: function () {
initShortcutHandlers(); initGlobalShortcuts();
var self = this; var self = this;
window.onkeyup = function(e) { window.onkeyup = function(e) {
var key = self.getKeyboardKey(e).toUpperCase(); var key = self._getKeyboardKey(e).toUpperCase();
var num = parseInt(key, 10); var num = parseInt(key, 10);
if(!($(":focus").is("input[type=text]") || if(!($(":focus").is("input[type=text]") ||
$(":focus").is("input[type=password]") || $(":focus").is("input[type=password]") ||
$(":focus").is("textarea"))) { $(":focus").is("textarea"))) {
if (shortcuts.hasOwnProperty(key)) { if (_shortcuts.hasOwnProperty(key)) {
shortcuts[key].function(e); _shortcuts[key].function(e);
} }
else if (!isNaN(num) && num >= 0 && num <= 9) { else if (!isNaN(num) && num >= 0 && num <= 9) {
APP.UI.clickOnVideo(num + 1); APP.UI.clickOnVideo(num + 1);
@ -101,7 +74,7 @@ var KeyboardShortcut = {
if(!($(":focus").is("input[type=text]") || if(!($(":focus").is("input[type=text]") ||
$(":focus").is("input[type=password]") || $(":focus").is("input[type=password]") ||
$(":focus").is("textarea"))) { $(":focus").is("textarea"))) {
var key = self.getKeyboardKey(e).toUpperCase(); var key = self._getKeyboardKey(e).toUpperCase();
if(key === "T") { if(key === "T") {
if(APP.conference.isLocalAudioMuted()) if(APP.conference.isLocalAudioMuted())
APP.conference.muteAudio(false); APP.conference.muteAudio(false);
@ -112,20 +85,56 @@ var KeyboardShortcut = {
trigger: 'click hover', trigger: 'click hover',
content: function() { content: function() {
return this.getAttribute("content") + return this.getAttribute("content") +
self.getShortcut(this.getAttribute("shortcut")); self._getShortcut(this.getAttribute("shortcut"));
} }
}); });
}, },
/**
* 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: function( shortcutChar,
shortcutAttr,
exec,
helpDescription) {
_shortcuts[shortcutChar] = {
character: shortcutChar,
shortcutAttr: 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: function(shortcutChar) {
_shortcuts.remove(shortcutChar);
},
/** /**
* *
* @param id indicates the popover associated with the shortcut * @param id indicates the popover associated with the shortcut
* @returns {string} the keyboard shortcut used for the id given * @returns {string} the keyboard shortcut used for the id given
*/ */
getShortcut: function (id) { _getShortcut: function (id) {
for (var key in shortcuts) { for (var key in _shortcuts) {
if (shortcuts.hasOwnProperty(key)) { if (_shortcuts.hasOwnProperty(key)) {
if (shortcuts[key].id === id) { if (_shortcuts[key].shortcutAttr === id) {
return " (" + shortcuts[key].character + ")"; return " (" + _shortcuts[key].character + ")";
} }
} }
} }
@ -135,7 +144,7 @@ var KeyboardShortcut = {
* @param e a KeyboardEvent * @param e a KeyboardEvent
* @returns {string} e.key or something close if not supported * @returns {string} e.key or something close if not supported
*/ */
getKeyboardKey: function (e) { _getKeyboardKey: function (e) {
if (typeof e.key === "string") { if (typeof e.key === "string") {
return e.key; return e.key;
} }
@ -156,6 +165,41 @@ var KeyboardShortcut = {
} else { } else {
return String.fromCharCode(e.which).toLowerCase(); 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: function (shortcutChar, shortcutDescriptionKey) {
var listElement = document.createElement("li");
var spanElement = document.createElement("span");
spanElement.className = "item-action";
var kbdElement = document.createElement("kbd");
kbdElement.className = "regular-key";
kbdElement.innerHTML = shortcutChar;
spanElement.appendChild(kbdElement);
var descriptionElement = document.createElement("span");
descriptionElement.className = "item-description";
descriptionElement.setAttribute("data-i18n", shortcutDescriptionKey);
descriptionElement.innerHTML
= APP.translation.translateString(shortcutDescriptionKey);
listElement.appendChild(spanElement);
listElement.appendChild(descriptionElement);
var parentListElement
= document.getElementById("keyboard-shortcuts-list");
if (parentListElement)
parentListElement.appendChild(listElement);
} }
}; };