location.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. import { supportsHistoryAPI } from '../utils/device.js'
  2. /**
  3. * Reads and writes the URL based on reveal.js' current state.
  4. */
  5. export default class Location {
  6. constructor( Reveal ) {
  7. this.Reveal = Reveal;
  8. // Delays updates to the URL due to a Chrome thumbnailer bug
  9. this.writeURLTimeout = 0;
  10. }
  11. /**
  12. * Reads the current URL (hash) and navigates accordingly.
  13. */
  14. readURL() {
  15. let config = this.Reveal.getConfig();
  16. let indices = this.Reveal.getIndices();
  17. let currentSlide = this.Reveal.getCurrentSlide();
  18. let hash = window.location.hash;
  19. // Attempt to parse the hash as either an index or name
  20. let bits = hash.slice( 2 ).split( '/' ),
  21. name = hash.replace( /#|\//gi, '' );
  22. // If the first bit is not fully numeric and there is a name we
  23. // can assume that this is a named link
  24. if( !/^[0-9]*$/.test( bits[0] ) && name.length ) {
  25. let element;
  26. // Ensure the named link is a valid HTML ID attribute
  27. try {
  28. element = document.getElementById( decodeURIComponent( name ) );
  29. }
  30. catch ( error ) { }
  31. // Ensure that we're not already on a slide with the same name
  32. let isSameNameAsCurrentSlide = currentSlide ? currentSlide.getAttribute( 'id' ) === name : false;
  33. if( element ) {
  34. // If the slide exists and is not the current slide...
  35. if ( !isSameNameAsCurrentSlide ) {
  36. // ...find the position of the named slide and navigate to it
  37. let elementIndex = this.Reveal.getIndices(element);
  38. this.Reveal.slide(elementIndex.h, elementIndex.v);
  39. }
  40. }
  41. // If the slide doesn't exist, navigate to the current slide
  42. else {
  43. this.Reveal.slide( indices.h || 0, indices.v || 0 );
  44. }
  45. }
  46. else {
  47. let hashIndexBase = config.hashOneBasedIndex ? 1 : 0;
  48. // Read the index components of the hash
  49. let h = ( parseInt( bits[0], 10 ) - hashIndexBase ) || 0,
  50. v = ( parseInt( bits[1], 10 ) - hashIndexBase ) || 0,
  51. f;
  52. if( config.fragmentInURL ) {
  53. f = parseInt( bits[2], 10 );
  54. if( isNaN( f ) ) {
  55. f = undefined;
  56. }
  57. }
  58. if( h !== indices.h || v !== indices.v || f !== undefined ) {
  59. this.Reveal.slide( h, v, f );
  60. }
  61. }
  62. }
  63. /**
  64. * Updates the page URL (hash) to reflect the current
  65. * state.
  66. *
  67. * @param {number} delay The time in ms to wait before
  68. * writing the hash
  69. */
  70. writeURL( delay ) {
  71. let config = this.Reveal.getConfig();
  72. let currentSlide = this.Reveal.getCurrentSlide();
  73. // Make sure there's never more than one timeout running
  74. clearTimeout( this.writeURLTimeout );
  75. // If a delay is specified, timeout this call
  76. if( typeof delay === 'number' ) {
  77. this.writeURLTimeout = setTimeout( this.writeURL, delay );
  78. }
  79. else if( currentSlide ) {
  80. // If we're configured to push to history OR the history
  81. // API is not avaialble.
  82. if( config.history || supportsHistoryAPI === false ) {
  83. window.location.hash = this.getHash();
  84. }
  85. // If we're configured to reflect the current slide in the
  86. // URL without pushing to history.
  87. else if( config.hash ) {
  88. window.history.replaceState( null, null, '#' + this.getHash() );
  89. }
  90. // If history and hash are both disabled, a hash may still
  91. // be added to the URL by clicking on a href with a hash
  92. // target. Counter this by always removing the hash.
  93. else {
  94. window.history.replaceState( null, null, window.location.pathname + window.location.search );
  95. }
  96. }
  97. }
  98. /**
  99. * Return a hash URL that will resolve to the given slide location.
  100. *
  101. * @param {HTMLElement} [slide=currentSlide] The slide to link to
  102. */
  103. getHash( slide ) {
  104. let url = '/';
  105. // Attempt to create a named link based on the slide's ID
  106. let s = slide || this.Reveal.getCurrentSlide();
  107. let id = s ? s.getAttribute( 'id' ) : null;
  108. if( id ) {
  109. id = encodeURIComponent( id );
  110. }
  111. let index = this.Reveal.getIndices( slide );
  112. if( !this.Reveal.getConfig().fragmentInURL ) {
  113. index.f = undefined;
  114. }
  115. // If the current slide has an ID, use that as a named link,
  116. // but we don't support named links with a fragment index
  117. if( typeof id === 'string' && id.length && index.f === undefined ) {
  118. url = '/' + id;
  119. }
  120. // Otherwise use the /h/v index
  121. else {
  122. let hashIndexBase = this.Reveal.getConfig().hashOneBasedIndex ? 1 : 0;
  123. if( index.h > 0 || index.v > 0 || index.f !== undefined ) url += index.h + hashIndexBase;
  124. if( index.v > 0 || index.f !== undefined ) url += '/' + (index.v + hashIndexBase );
  125. if( index.f !== undefined ) url += '/' + index.f;
  126. }
  127. return url;
  128. }
  129. }