new shortcuts; alt+arrow key skips fragments, shift+arrow key jumps to last slide in the given direction #1105
parent
a3e8162eb1
commit
90bbe8be4f
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -32,15 +32,15 @@ export default class Keyboard {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.shortcuts['N , SPACE'] = 'Next slide';
|
this.shortcuts['N , SPACE'] = 'Next slide';
|
||||||
this.shortcuts['P'] = 'Previous slide';
|
this.shortcuts['P , Shift SPACE'] = 'Previous slide';
|
||||||
this.shortcuts['← , H'] = 'Navigate left';
|
this.shortcuts['← , H'] = 'Navigate left';
|
||||||
this.shortcuts['→ , L'] = 'Navigate right';
|
this.shortcuts['→ , L'] = 'Navigate right';
|
||||||
this.shortcuts['↑ , K'] = 'Navigate up';
|
this.shortcuts['↑ , K'] = 'Navigate up';
|
||||||
this.shortcuts['↓ , J'] = 'Navigate down';
|
this.shortcuts['↓ , J'] = 'Navigate down';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.shortcuts['Home , Shift ←'] = 'First slide';
|
this.shortcuts['Alt + ←/↑/→/↓'] = 'Navigate without fragments';
|
||||||
this.shortcuts['End , Shift →'] = 'Last slide';
|
this.shortcuts['Shift + ←/↑/→/↓'] = 'Jump to first/last slide';
|
||||||
this.shortcuts['B , .'] = 'Pause';
|
this.shortcuts['B , .'] = 'Pause';
|
||||||
this.shortcuts['F'] = 'Fullscreen';
|
this.shortcuts['F'] = 'Fullscreen';
|
||||||
this.shortcuts['ESC, O'] = 'Slide overview';
|
this.shortcuts['ESC, O'] = 'Slide overview';
|
||||||
|
@ -182,13 +182,11 @@ export default class Keyboard {
|
||||||
let activeElementIsInput = document.activeElement && document.activeElement.tagName && /input|textarea/i.test( document.activeElement.tagName );
|
let activeElementIsInput = document.activeElement && document.activeElement.tagName && /input|textarea/i.test( document.activeElement.tagName );
|
||||||
let activeElementIsNotes = document.activeElement && document.activeElement.className && /speaker-notes/i.test( document.activeElement.className);
|
let activeElementIsNotes = document.activeElement && document.activeElement.className && /speaker-notes/i.test( document.activeElement.className);
|
||||||
|
|
||||||
// Whitelist specific modified + keycode combinations
|
// Whitelist certain modifiers for slide navigation shortcuts
|
||||||
let prevSlideShortcut = event.shiftKey && event.keyCode === 32;
|
let isNavigationKey = [32, 37, 38, 39, 40, 78, 80].indexOf( event.keyCode ) !== -1;
|
||||||
let firstSlideShortcut = event.shiftKey && keyCode === 37;
|
|
||||||
let lastSlideShortcut = event.shiftKey && keyCode === 39;
|
|
||||||
|
|
||||||
// Prevent all other events when a modifier is pressed
|
// Prevent all other events when a modifier is pressed
|
||||||
let unusedModifier = !prevSlideShortcut && !firstSlideShortcut && !lastSlideShortcut &&
|
let unusedModifier = !( isNavigationKey && event.shiftKey || event.altKey ) &&
|
||||||
( event.shiftKey || event.altKey || event.ctrlKey || event.metaKey );
|
( event.shiftKey || event.altKey || event.ctrlKey || event.metaKey );
|
||||||
|
|
||||||
// Disregard the event if there's a focused element or a
|
// Disregard the event if there's a focused element or a
|
||||||
|
@ -277,52 +275,58 @@ export default class Keyboard {
|
||||||
|
|
||||||
// P, PAGE UP
|
// P, PAGE UP
|
||||||
if( keyCode === 80 || keyCode === 33 ) {
|
if( keyCode === 80 || keyCode === 33 ) {
|
||||||
this.Reveal.prev();
|
this.Reveal.prev({skipFragments: event.altKey});
|
||||||
}
|
}
|
||||||
// N, PAGE DOWN
|
// N, PAGE DOWN
|
||||||
else if( keyCode === 78 || keyCode === 34 ) {
|
else if( keyCode === 78 || keyCode === 34 ) {
|
||||||
this.Reveal.next();
|
this.Reveal.next({skipFragments: event.altKey});
|
||||||
}
|
}
|
||||||
// H, LEFT
|
// H, LEFT
|
||||||
else if( keyCode === 72 || keyCode === 37 ) {
|
else if( keyCode === 72 || keyCode === 37 ) {
|
||||||
if( firstSlideShortcut ) {
|
if( event.shiftKey ) {
|
||||||
this.Reveal.slide( 0 );
|
this.Reveal.slide( 0 );
|
||||||
}
|
}
|
||||||
else if( !this.Reveal.overview.isActive() && useLinearMode ) {
|
else if( !this.Reveal.overview.isActive() && useLinearMode ) {
|
||||||
this.Reveal.prev();
|
this.Reveal.prev({skipFragments: event.altKey});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.Reveal.left();
|
this.Reveal.left({skipFragments: event.altKey});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// L, RIGHT
|
// L, RIGHT
|
||||||
else if( keyCode === 76 || keyCode === 39 ) {
|
else if( keyCode === 76 || keyCode === 39 ) {
|
||||||
if( lastSlideShortcut ) {
|
if( event.shiftKey ) {
|
||||||
this.Reveal.slide( Number.MAX_VALUE );
|
this.Reveal.slide( Number.MAX_VALUE );
|
||||||
}
|
}
|
||||||
else if( !this.Reveal.overview.isActive() && useLinearMode ) {
|
else if( !this.Reveal.overview.isActive() && useLinearMode ) {
|
||||||
this.Reveal.next();
|
this.Reveal.next({skipFragments: event.altKey});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.Reveal.right();
|
this.Reveal.right({skipFragments: event.altKey});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// K, UP
|
// K, UP
|
||||||
else if( keyCode === 75 || keyCode === 38 ) {
|
else if( keyCode === 75 || keyCode === 38 ) {
|
||||||
if( !this.Reveal.overview.isActive() && useLinearMode ) {
|
if( event.shiftKey ) {
|
||||||
this.Reveal.prev();
|
this.Reveal.slide( undefined, 0 );
|
||||||
|
}
|
||||||
|
else if( !this.Reveal.overview.isActive() && useLinearMode ) {
|
||||||
|
this.Reveal.prev({skipFragments: event.altKey});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.Reveal.up();
|
this.Reveal.up({skipFragments: event.altKey});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// J, DOWN
|
// J, DOWN
|
||||||
else if( keyCode === 74 || keyCode === 40 ) {
|
else if( keyCode === 74 || keyCode === 40 ) {
|
||||||
if( !this.Reveal.overview.isActive() && useLinearMode ) {
|
if( event.shiftKey ) {
|
||||||
this.Reveal.next();
|
this.Reveal.slide( undefined, Number.MAX_VALUE );
|
||||||
|
}
|
||||||
|
else if( !this.Reveal.overview.isActive() && useLinearMode ) {
|
||||||
|
this.Reveal.next({skipFragments: event.altKey});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.Reveal.down();
|
this.Reveal.down({skipFragments: event.altKey});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// HOME
|
// HOME
|
||||||
|
@ -339,10 +343,10 @@ export default class Keyboard {
|
||||||
this.Reveal.overview.deactivate();
|
this.Reveal.overview.deactivate();
|
||||||
}
|
}
|
||||||
if( event.shiftKey ) {
|
if( event.shiftKey ) {
|
||||||
this.Reveal.prev();
|
this.Reveal.prev({skipFragments: event.altKey});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.Reveal.next();
|
this.Reveal.next({skipFragments: event.altKey});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TWO-SPOT, SEMICOLON, B, V, PERIOD, LOGITECH PRESENTER TOOLS "BLACK SCREEN" BUTTON
|
// TWO-SPOT, SEMICOLON, B, V, PERIOD, LOGITECH PRESENTER TOOLS "BLACK SCREEN" BUTTON
|
||||||
|
|
36
js/reveal.js
36
js/reveal.js
|
@ -2197,55 +2197,55 @@ export default function( revealElement, options ) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateLeft() {
|
function navigateLeft({skipFragments=false}={}) {
|
||||||
|
|
||||||
navigationHistory.hasNavigatedHorizontally = true;
|
navigationHistory.hasNavigatedHorizontally = true;
|
||||||
|
|
||||||
// Reverse for RTL
|
// Reverse for RTL
|
||||||
if( config.rtl ) {
|
if( config.rtl ) {
|
||||||
if( ( overview.isActive() || fragments.next() === false ) && availableRoutes().left ) {
|
if( ( overview.isActive() || skipFragments || fragments.next() === false ) && availableRoutes().left ) {
|
||||||
slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined );
|
slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Normal navigation
|
// Normal navigation
|
||||||
else if( ( overview.isActive() || fragments.prev() === false ) && availableRoutes().left ) {
|
else if( ( overview.isActive() || skipFragments || fragments.prev() === false ) && availableRoutes().left ) {
|
||||||
slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined );
|
slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateRight() {
|
function navigateRight({skipFragments=false}={}) {
|
||||||
|
|
||||||
navigationHistory.hasNavigatedHorizontally = true;
|
navigationHistory.hasNavigatedHorizontally = true;
|
||||||
|
|
||||||
// Reverse for RTL
|
// Reverse for RTL
|
||||||
if( config.rtl ) {
|
if( config.rtl ) {
|
||||||
if( ( overview.isActive() || fragments.prev() === false ) && availableRoutes().right ) {
|
if( ( overview.isActive() || skipFragments || fragments.prev() === false ) && availableRoutes().right ) {
|
||||||
slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined );
|
slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Normal navigation
|
// Normal navigation
|
||||||
else if( ( overview.isActive() || fragments.next() === false ) && availableRoutes().right ) {
|
else if( ( overview.isActive() || skipFragments || fragments.next() === false ) && availableRoutes().right ) {
|
||||||
slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined );
|
slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateUp() {
|
function navigateUp({skipFragments=false}={}) {
|
||||||
|
|
||||||
// Prioritize hiding fragments
|
// Prioritize hiding fragments
|
||||||
if( ( overview.isActive() || fragments.prev() === false ) && availableRoutes().up ) {
|
if( ( overview.isActive() || skipFragments || fragments.prev() === false ) && availableRoutes().up ) {
|
||||||
slide( indexh, indexv - 1 );
|
slide( indexh, indexv - 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function navigateDown() {
|
function navigateDown({skipFragments=false}={}) {
|
||||||
|
|
||||||
navigationHistory.hasNavigatedVertically = true;
|
navigationHistory.hasNavigatedVertically = true;
|
||||||
|
|
||||||
// Prioritize revealing fragments
|
// Prioritize revealing fragments
|
||||||
if( ( overview.isActive() || fragments.next() === false ) && availableRoutes().down ) {
|
if( ( overview.isActive() || skipFragments || fragments.next() === false ) && availableRoutes().down ) {
|
||||||
slide( indexh, indexv + 1 );
|
slide( indexh, indexv + 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2257,12 +2257,12 @@ export default function( revealElement, options ) {
|
||||||
* 2) Previous vertical slide
|
* 2) Previous vertical slide
|
||||||
* 3) Previous horizontal slide
|
* 3) Previous horizontal slide
|
||||||
*/
|
*/
|
||||||
function navigatePrev() {
|
function navigatePrev({skipFragments=false}={}) {
|
||||||
|
|
||||||
// Prioritize revealing fragments
|
// Prioritize revealing fragments
|
||||||
if( fragments.prev() === false ) {
|
if( skipFragments || fragments.prev() === false ) {
|
||||||
if( availableRoutes().up ) {
|
if( availableRoutes().up ) {
|
||||||
navigateUp();
|
navigateUp({skipFragments});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Fetch the previous horizontal slide, if there is one
|
// Fetch the previous horizontal slide, if there is one
|
||||||
|
@ -2288,13 +2288,13 @@ export default function( revealElement, options ) {
|
||||||
/**
|
/**
|
||||||
* The reverse of #navigatePrev().
|
* The reverse of #navigatePrev().
|
||||||
*/
|
*/
|
||||||
function navigateNext() {
|
function navigateNext({skipFragments=false}={}) {
|
||||||
|
|
||||||
navigationHistory.hasNavigatedHorizontally = true;
|
navigationHistory.hasNavigatedHorizontally = true;
|
||||||
navigationHistory.hasNavigatedVertically = true;
|
navigationHistory.hasNavigatedVertically = true;
|
||||||
|
|
||||||
// Prioritize revealing fragments
|
// Prioritize revealing fragments
|
||||||
if( fragments.next() === false ) {
|
if( skipFragments || fragments.next() === false ) {
|
||||||
|
|
||||||
let routes = availableRoutes();
|
let routes = availableRoutes();
|
||||||
|
|
||||||
|
@ -2306,13 +2306,13 @@ export default function( revealElement, options ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if( routes.down ) {
|
if( routes.down ) {
|
||||||
navigateDown();
|
navigateDown({skipFragments});
|
||||||
}
|
}
|
||||||
else if( config.rtl ) {
|
else if( config.rtl ) {
|
||||||
navigateLeft();
|
navigateLeft({skipFragments});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
navigateRight();
|
navigateRight({skipFragments});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,11 @@
|
||||||
// 4
|
// 4
|
||||||
Reveal.initialize().then( function() {
|
Reveal.initialize().then( function() {
|
||||||
|
|
||||||
|
// Helper methods
|
||||||
|
function triggerKeyboardEvent(config) {
|
||||||
|
document.dispatchEvent( new KeyboardEvent( 'keydown', config ) );
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// DOM TESTS
|
// DOM TESTS
|
||||||
|
|
||||||
|
@ -407,13 +412,41 @@
|
||||||
assert.ok( /X\-SHORTCUT\-X/.test( document.body.innerHTML ), 'binding is added to help overlay' );
|
assert.ok( /X\-SHORTCUT\-X/.test( document.body.innerHTML ), 'binding is added to help overlay' );
|
||||||
Reveal.toggleHelp( false );
|
Reveal.toggleHelp( false );
|
||||||
|
|
||||||
let event = new KeyboardEvent( 'keydown', { 'keyCode':88 } );
|
triggerKeyboardEvent({ keyCode: 88 });
|
||||||
document.dispatchEvent( event );
|
|
||||||
|
|
||||||
Reveal.removeKeyBinding( 88 );
|
Reveal.removeKeyBinding( 88 );
|
||||||
|
|
||||||
// should do nothing
|
// should do nothing
|
||||||
document.dispatchEvent( event );
|
triggerKeyboardEvent({ keyCode: 88 });
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test( 'Navigation bindings', function( assert ) {
|
||||||
|
Reveal.slide( 0 );
|
||||||
|
|
||||||
|
// right arrow
|
||||||
|
triggerKeyboardEvent({ keyCode: 39 });
|
||||||
|
assert.strictEqual( Reveal.getIndices().h, 1 );
|
||||||
|
|
||||||
|
// down arrow + shift
|
||||||
|
triggerKeyboardEvent({ keyCode: 40, shiftKey: true });
|
||||||
|
assert.strictEqual( Reveal.getIndices().v, 2, 'shift + down arrow goes to last vertical slide' );
|
||||||
|
|
||||||
|
// up arrow
|
||||||
|
triggerKeyboardEvent({ keyCode: 38 });
|
||||||
|
assert.strictEqual( Reveal.getIndices().v, 1 );
|
||||||
|
|
||||||
|
// right arrow + shift
|
||||||
|
triggerKeyboardEvent({ keyCode: 39, shiftKey: true });
|
||||||
|
assert.ok( Reveal.isLastSlide(), 'shift + right arrow goes to last horizontal slide' );
|
||||||
|
|
||||||
|
|
||||||
|
// right arrow on slide with fragments
|
||||||
|
Reveal.slide( 2, 0, -1 );
|
||||||
|
triggerKeyboardEvent({ keyCode: 39 });
|
||||||
|
assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 }, 'right arrow shows fragment' );
|
||||||
|
|
||||||
|
triggerKeyboardEvent({ keyCode: 39, altKey: true });
|
||||||
|
assert.strictEqual( Reveal.getIndices().h, 3, 'right arrow skips fragments when alt key is pressed' );
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue