| 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 ); |
| |
| } |
| |
| } |