jumptoslide.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import {
  2. SLIDE_NUMBER_FORMAT_CURRENT,
  3. SLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL
  4. } from "../utils/constants";
  5. /**
  6. * Makes it possible to jump to a slide by entering its
  7. * slide number or id.
  8. */
  9. export default class JumpToSlide {
  10. constructor( Reveal ) {
  11. this.Reveal = Reveal;
  12. this.onInput = this.onInput.bind( this );
  13. this.onBlur = this.onBlur.bind( this );
  14. this.onKeyDown = this.onKeyDown.bind( this );
  15. }
  16. render() {
  17. this.element = document.createElement( 'div' );
  18. this.element.className = 'jump-to-slide';
  19. this.jumpInput = document.createElement( 'input' );
  20. this.jumpInput.type = 'text';
  21. this.jumpInput.className = 'jump-to-slide-input';
  22. this.jumpInput.placeholder = 'Jump to slide';
  23. this.jumpInput.addEventListener( 'input', this.onInput );
  24. this.jumpInput.addEventListener( 'keydown', this.onKeyDown );
  25. this.jumpInput.addEventListener( 'blur', this.onBlur );
  26. this.element.appendChild( this.jumpInput );
  27. }
  28. show() {
  29. this.indicesOnShow = this.Reveal.getIndices();
  30. this.Reveal.getRevealElement().appendChild( this.element );
  31. this.jumpInput.focus();
  32. }
  33. hide() {
  34. if( this.isVisible() ) {
  35. this.element.remove();
  36. this.jumpInput.value = '';
  37. clearTimeout( this.jumpTimeout );
  38. delete this.jumpTimeout;
  39. }
  40. }
  41. isVisible() {
  42. return !!this.element.parentNode;
  43. }
  44. /**
  45. * Parses the current input and jumps to the given slide.
  46. */
  47. jump() {
  48. clearTimeout( this.jumpTimeout );
  49. delete this.jumpTimeout;
  50. let query = this.jumpInput.value.trim( '' );
  51. let indices;
  52. // When slide numbers are formatted to be a single linear number
  53. // (instead of showing a separate horizontal/vertical index) we
  54. // use the same format for slide jumps
  55. if( /^\d+$/.test( query ) ) {
  56. const slideNumberFormat = this.Reveal.getConfig().slideNumber;
  57. if( slideNumberFormat === SLIDE_NUMBER_FORMAT_CURRENT || slideNumberFormat === SLIDE_NUMBER_FORMAT_CURRENT_SLASH_TOTAL ) {
  58. const slide = this.Reveal.getSlides()[ parseInt( query, 10 ) - 1 ];
  59. if( slide ) {
  60. indices = this.Reveal.getIndices( slide );
  61. }
  62. }
  63. }
  64. if( !indices ) {
  65. // If the query uses "horizontal.vertical" format, convert to
  66. // "horizontal/vertical" so that our URL parser can understand
  67. if( /^\d+\.\d+$/.test( query ) ) {
  68. query = query.replace( '.', '/' );
  69. }
  70. indices = this.Reveal.location.getIndicesFromHash( query, { oneBasedIndex: true } );
  71. }
  72. // Still no valid index? Fall back on a text search
  73. if( !indices && /\S+/i.test( query ) && query.length > 1 ) {
  74. indices = this.search( query );
  75. }
  76. if( indices && query !== '' ) {
  77. this.Reveal.slide( indices.h, indices.v, indices.f );
  78. return true;
  79. }
  80. else {
  81. this.Reveal.slide( this.indicesOnShow.h, this.indicesOnShow.v, this.indicesOnShow.f );
  82. return false;
  83. }
  84. }
  85. jumpAfter( delay ) {
  86. clearTimeout( this.jumpTimeout );
  87. this.jumpTimeout = setTimeout( () => this.jump(), delay );
  88. }
  89. /**
  90. * A lofi search that looks for the given query in all
  91. * of our slides and returns the first match.
  92. */
  93. search( query ) {
  94. const regex = new RegExp( '\\b' + query.trim() + '\\b', 'i' );
  95. const slide = this.Reveal.getSlides().find( ( slide ) => {
  96. return regex.test( slide.innerText );
  97. } );
  98. if( slide ) {
  99. return this.Reveal.getIndices( slide );
  100. }
  101. else {
  102. return null;
  103. }
  104. }
  105. /**
  106. * Reverts back to the slide we were on when jump to slide was
  107. * invoked.
  108. */
  109. cancel() {
  110. this.Reveal.slide( this.indicesOnShow.h, this.indicesOnShow.v, this.indicesOnShow.f );
  111. this.hide();
  112. }
  113. confirm() {
  114. this.jump();
  115. this.hide();
  116. }
  117. destroy() {
  118. this.jumpInput.removeEventListener( 'input', this.onInput );
  119. this.jumpInput.removeEventListener( 'keydown', this.onKeyDown );
  120. this.jumpInput.removeEventListener( 'blur', this.onBlur );
  121. this.element.remove();
  122. }
  123. onKeyDown( event ) {
  124. if( event.keyCode === 13 ) {
  125. this.confirm();
  126. }
  127. else if( event.keyCode === 27 ) {
  128. this.cancel();
  129. event.stopImmediatePropagation();
  130. }
  131. }
  132. onInput( event ) {
  133. this.jumpAfter( 200 );
  134. }
  135. onBlur() {
  136. setTimeout( () => this.hide(), 1 );
  137. }
  138. }