/** * 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; const query = this.jumpInput.value.trim( '' ); let indices = this.Reveal.location.getIndicesFromHash( query, { oneBasedIndex: true } ); // If no valid index was found and the input query is a // string, fall back on a simple 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 ); } }