197 lines
4.0 KiB
JavaScript
197 lines
4.0 KiB
JavaScript
|
import {
|
||
|
SLIDE_NUMBER_FORMAT_CURRENT,
|
||
|
SLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL
|
||
|
} from "../utils/constants";
|
||
|
|
||
|
/**
|
||
|
* Makes it possible to jump to a slide by entering its
|
||
|
* slide number or id.
|
||
|
*/
|
||
|
export default class JumpToSlide {
|
||
|
|
||
|
constructor( Reveal ) {
|
||
|
|
||
|
this.Reveal = Reveal;
|
||
|
|
||
|
this.onInput = this.onInput.bind( this );
|
||
|
this.onBlur = this.onBlur.bind( this );
|
||
|
this.onKeyDown = this.onKeyDown.bind( this );
|
||
|
|
||
|
}
|
||
|
|
||
|
render() {
|
||
|
|
||
|
this.element = document.createElement( 'div' );
|
||
|
this.element.className = 'jump-to-slide';
|
||
|
|
||
|
this.jumpInput = document.createElement( 'input' );
|
||
|
this.jumpInput.type = 'text';
|
||
|
this.jumpInput.className = 'jump-to-slide-input';
|
||
|
this.jumpInput.placeholder = 'Jump to slide';
|
||
|
this.jumpInput.addEventListener( 'input', this.onInput );
|
||
|
this.jumpInput.addEventListener( 'keydown', this.onKeyDown );
|
||
|
this.jumpInput.addEventListener( 'blur', this.onBlur );
|
||
|
|
||
|
this.element.appendChild( this.jumpInput );
|
||
|
|
||
|
}
|
||
|
|
||
|
show() {
|
||
|
|
||
|
this.indicesOnShow = this.Reveal.getIndices();
|
||
|
|
||
|
this.Reveal.getRevealElement().appendChild( this.element );
|
||
|
this.jumpInput.focus();
|
||
|
|
||
|
}
|
||
|
|
||
|
hide() {
|
||
|
|
||
|
if( this.isVisible() ) {
|
||
|
this.element.remove();
|
||
|
this.jumpInput.value = '';
|
||
|
|
||
|
clearTimeout( this.jumpTimeout );
|
||
|
delete this.jumpTimeout;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
isVisible() {
|
||
|
|
||
|
return !!this.element.parentNode;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parses the current input and jumps to the given slide.
|
||
|
*/
|
||
|
jump() {
|
||
|
|
||
|
clearTimeout( this.jumpTimeout );
|
||
|
delete this.jumpTimeout;
|
||
|
|
||
|
let query = this.jumpInput.value.trim( '' );
|
||
|
let indices;
|
||
|
|
||
|
// When slide numbers are formatted to be a single linear mumber
|
||
|
// (instead of showing a separate horizontal/vertical index) we
|
||
|
// use the same format for slide jumps
|
||
|
if( /^\d+$/.test( query ) ) {
|
||
|
const slideNumberFormat = this.Reveal.getConfig().slideNumber;
|
||
|
if( slideNumberFormat === SLIDE_NUMBER_FORMAT_CURRENT || slideNumberFormat === SLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL ) {
|
||
|
const slide = this.Reveal.getSlides()[ parseInt( query, 10 ) - 1 ];
|
||
|
if( slide ) {
|
||
|
indices = this.Reveal.getIndices( slide );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( !indices ) {
|
||
|
// If the query uses "horizontal.vertical" format, convert to
|
||
|
// "horizontal/vertical" so that our URL parser can understand
|
||
|
if( /^\d+\.\d+$/.test( query ) ) {
|
||
|
query = query.replace( '.', '/' );
|
||
|
}
|
||
|
|
||
|
indices = this.Reveal.location.getIndicesFromHash( query, { oneBasedIndex: true } );
|
||
|
}
|
||
|
|
||
|
// Still no valid index? Fall back on a text search
|
||
|
if( !indices && /\S+/i.test( query ) && query.length > 1 ) {
|
||
|
indices = this.search( query );
|
||
|
}
|
||
|
|
||
|
if( indices && query !== '' ) {
|
||
|
this.Reveal.slide( indices.h, indices.v, indices.f );
|
||
|
return true;
|
||
|
}
|
||
|
else {
|
||
|
this.Reveal.slide( this.indicesOnShow.h, this.indicesOnShow.v, this.indicesOnShow.f );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
jumpAfter( delay ) {
|
||
|
|
||
|
clearTimeout( this.jumpTimeout );
|
||
|
this.jumpTimeout = setTimeout( () => this.jump(), delay );
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A lofi search that looks for the given query in all
|
||
|
* of our slides and returns the first match.
|
||
|
*/
|
||
|
search( query ) {
|
||
|
|
||
|
const regex = new RegExp( '\\b' + query.trim() + '\\b', 'i' );
|
||
|
|
||
|
const slide = this.Reveal.getSlides().find( ( slide ) => {
|
||
|
return regex.test( slide.innerText );
|
||
|
} );
|
||
|
|
||
|
if( slide ) {
|
||
|
return this.Reveal.getIndices( slide );
|
||
|
}
|
||
|
else {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reverts back to the slide we were on when jump to slide was
|
||
|
* invoked.
|
||
|
*/
|
||
|
cancel() {
|
||
|
|
||
|
this.Reveal.slide( this.indicesOnShow.h, this.indicesOnShow.v, this.indicesOnShow.f );
|
||
|
this.hide();
|
||
|
|
||
|
}
|
||
|
|
||
|
confirm() {
|
||
|
|
||
|
this.jump();
|
||
|
this.hide();
|
||
|
|
||
|
}
|
||
|
|
||
|
destroy() {
|
||
|
|
||
|
this.jumpInput.removeEventListener( 'input', this.onInput );
|
||
|
this.jumpInput.removeEventListener( 'keydown', this.onKeyDown );
|
||
|
this.jumpInput.removeEventListener( 'blur', this.onBlur );
|
||
|
|
||
|
this.element.remove();
|
||
|
|
||
|
}
|
||
|
|
||
|
onKeyDown( event ) {
|
||
|
|
||
|
if( event.keyCode === 13 ) {
|
||
|
this.confirm();
|
||
|
}
|
||
|
else if( event.keyCode === 27 ) {
|
||
|
this.cancel();
|
||
|
|
||
|
event.stopImmediatePropagation();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
onInput( event ) {
|
||
|
|
||
|
this.jumpAfter( 200 );
|
||
|
|
||
|
}
|
||
|
|
||
|
onBlur() {
|
||
|
|
||
|
setTimeout( () => this.hide(), 1 );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|