159 lines
4.4 KiB
JavaScript
159 lines
4.4 KiB
JavaScript
import { supportsHistoryAPI } from '../utils/device.js'
|
|
|
|
/**
|
|
* Reads and writes the URL based on reveal.js' current state.
|
|
*/
|
|
export default class Location {
|
|
|
|
constructor( Reveal ) {
|
|
|
|
this.Reveal = Reveal;
|
|
|
|
// Delays updates to the URL due to a Chrome thumbnailer bug
|
|
this.writeURLTimeout = 0;
|
|
|
|
}
|
|
|
|
/**
|
|
* Reads the current URL (hash) and navigates accordingly.
|
|
*/
|
|
readURL() {
|
|
|
|
let config = this.Reveal.getConfig();
|
|
let indices = this.Reveal.getIndices();
|
|
let currentSlide = this.Reveal.getCurrentSlide();
|
|
|
|
let hash = window.location.hash;
|
|
|
|
// Attempt to parse the hash as either an index or name
|
|
let bits = hash.slice( 2 ).split( '/' ),
|
|
name = hash.replace( /#|\//gi, '' );
|
|
|
|
// If the first bit is not fully numeric and there is a name we
|
|
// can assume that this is a named link
|
|
if( !/^[0-9]*$/.test( bits[0] ) && name.length ) {
|
|
let element;
|
|
|
|
// Ensure the named link is a valid HTML ID attribute
|
|
try {
|
|
element = document.getElementById( decodeURIComponent( name ) );
|
|
}
|
|
catch ( error ) { }
|
|
|
|
// Ensure that we're not already on a slide with the same name
|
|
let isSameNameAsCurrentSlide = currentSlide ? currentSlide.getAttribute( 'id' ) === name : false;
|
|
|
|
if( element ) {
|
|
// If the slide exists and is not the current slide...
|
|
if ( !isSameNameAsCurrentSlide ) {
|
|
// ...find the position of the named slide and navigate to it
|
|
let elementIndex = this.Reveal.getIndices(element);
|
|
this.Reveal.slide(elementIndex.h, elementIndex.v);
|
|
}
|
|
}
|
|
// If the slide doesn't exist, navigate to the current slide
|
|
else {
|
|
this.Reveal.slide( indices.h || 0, indices.v || 0 );
|
|
}
|
|
}
|
|
else {
|
|
let hashIndexBase = config.hashOneBasedIndex ? 1 : 0;
|
|
|
|
// Read the index components of the hash
|
|
let h = ( parseInt( bits[0], 10 ) - hashIndexBase ) || 0,
|
|
v = ( parseInt( bits[1], 10 ) - hashIndexBase ) || 0,
|
|
f;
|
|
|
|
if( config.fragmentInURL ) {
|
|
f = parseInt( bits[2], 10 );
|
|
if( isNaN( f ) ) {
|
|
f = undefined;
|
|
}
|
|
}
|
|
|
|
if( h !== indices.h || v !== indices.v || f !== undefined ) {
|
|
this.Reveal.slide( h, v, f );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Updates the page URL (hash) to reflect the current
|
|
* state.
|
|
*
|
|
* @param {number} delay The time in ms to wait before
|
|
* writing the hash
|
|
*/
|
|
writeURL( delay ) {
|
|
|
|
let config = this.Reveal.getConfig();
|
|
let currentSlide = this.Reveal.getCurrentSlide();
|
|
|
|
// Make sure there's never more than one timeout running
|
|
clearTimeout( this.writeURLTimeout );
|
|
|
|
// If a delay is specified, timeout this call
|
|
if( typeof delay === 'number' ) {
|
|
this.writeURLTimeout = setTimeout( this.writeURL, delay );
|
|
}
|
|
else if( currentSlide ) {
|
|
// If we're configured to push to history OR the history
|
|
// API is not avaialble.
|
|
if( config.history || supportsHistoryAPI === false ) {
|
|
window.location.hash = this.getHash();
|
|
}
|
|
// If we're configured to reflect the current slide in the
|
|
// URL without pushing to history.
|
|
else if( config.hash ) {
|
|
window.history.replaceState( null, null, '#' + this.getHash() );
|
|
}
|
|
// If history and hash are both disabled, a hash may still
|
|
// be added to the URL by clicking on a href with a hash
|
|
// target. Counter this by always removing the hash.
|
|
else {
|
|
window.history.replaceState( null, null, window.location.pathname + window.location.search );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Return a hash URL that will resolve to the given slide location.
|
|
*
|
|
* @param {HTMLElement} [slide=currentSlide] The slide to link to
|
|
*/
|
|
getHash( slide ) {
|
|
|
|
let url = '/';
|
|
|
|
// Attempt to create a named link based on the slide's ID
|
|
let s = slide || this.Reveal.getCurrentSlide();
|
|
let id = s ? s.getAttribute( 'id' ) : null;
|
|
if( id ) {
|
|
id = encodeURIComponent( id );
|
|
}
|
|
|
|
let index = this.Reveal.getIndices( slide );
|
|
if( !this.Reveal.getConfig().fragmentInURL ) {
|
|
index.f = undefined;
|
|
}
|
|
|
|
// If the current slide has an ID, use that as a named link,
|
|
// but we don't support named links with a fragment index
|
|
if( typeof id === 'string' && id.length && index.f === undefined ) {
|
|
url = '/' + id;
|
|
}
|
|
// Otherwise use the /h/v index
|
|
else {
|
|
let hashIndexBase = this.Reveal.getConfig().hashOneBasedIndex ? 1 : 0;
|
|
if( index.h > 0 || index.v > 0 || index.f !== undefined ) url += index.h + hashIndexBase;
|
|
if( index.v > 0 || index.f !== undefined ) url += '/' + (index.v + hashIndexBase );
|
|
if( index.f !== undefined ) url += '/' + index.f;
|
|
}
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
} |