add slidenumber & location controllers
This commit is contained in:
parent
ac15678dea
commit
3683ad255d
2
dist/reveal.min.js
vendored
2
dist/reveal.min.js
vendored
File diff suppressed because one or more lines are too long
@ -263,7 +263,7 @@ export default class Keyboard {
|
||||
}
|
||||
// N, PAGE DOWN
|
||||
else if( keyCode === 78 || keyCode === 34 ) {
|
||||
this.Review.next();
|
||||
this.Reveal.next();
|
||||
}
|
||||
// H, LEFT
|
||||
else if( keyCode === 72 || keyCode === 37 ) {
|
||||
@ -283,7 +283,7 @@ export default class Keyboard {
|
||||
this.Reveal.slide( Number.MAX_VALUE );
|
||||
}
|
||||
else if( !this.Reveal.overview.isActive() && useLinearMode ) {
|
||||
this.Review.next();
|
||||
this.Reveal.next();
|
||||
}
|
||||
else {
|
||||
this.Reveal.right();
|
||||
@ -301,7 +301,7 @@ export default class Keyboard {
|
||||
// J, DOWN
|
||||
else if( keyCode === 74 || keyCode === 40 ) {
|
||||
if( !this.Reveal.overview.isActive() && useLinearMode ) {
|
||||
this.Review.next();
|
||||
this.Reveal.next();
|
||||
}
|
||||
else {
|
||||
this.Reveal.down();
|
||||
@ -324,7 +324,7 @@ export default class Keyboard {
|
||||
this.Reveal.prev();
|
||||
}
|
||||
else {
|
||||
this.Review.next();
|
||||
this.Reveal.next();
|
||||
}
|
||||
}
|
||||
// TWO-SPOT, SEMICOLON, B, V, PERIOD, LOGITECH PRESENTER TOOLS "BLACK SCREEN" BUTTON
|
||||
|
51
js/controllers/location.js
Normal file
51
js/controllers/location.js
Normal file
@ -0,0 +1,51 @@
|
||||
import { enterFullscreen } from '../utils/util.js'
|
||||
|
||||
/**
|
||||
* Handles all reveal.js keyboard interactions.
|
||||
*/
|
||||
export default class Location {
|
||||
|
||||
constructor( Reveal ) {
|
||||
|
||||
this.Reveal = Reveal;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash URL that will resolve to the given slide location.
|
||||
*
|
||||
* @param {HTMLElement} [slide=currentSlide] The slide to link to
|
||||
*/
|
||||
getHash( slide = this.Reveal.getCurrentSlide() ) {
|
||||
|
||||
let url = '/';
|
||||
|
||||
// Attempt to create a named link based on the slide's ID
|
||||
let id = slide ? slide.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;
|
||||
|
||||
}
|
||||
|
||||
}
|
128
js/controllers/slidenumber.js
Normal file
128
js/controllers/slidenumber.js
Normal file
@ -0,0 +1,128 @@
|
||||
import { enterFullscreen } from '../utils/util.js'
|
||||
|
||||
/**
|
||||
* Handles all reveal.js keyboard interactions.
|
||||
*/
|
||||
export default class SlideNumber {
|
||||
|
||||
constructor( Reveal ) {
|
||||
|
||||
this.Reveal = Reveal;
|
||||
|
||||
}
|
||||
|
||||
createElement() {
|
||||
|
||||
this.element = document.createElement( 'div' );
|
||||
this.element.className = 'slide-number';
|
||||
this.Reveal.getRevealElement().appendChild( this.element );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides the slide number depending on the
|
||||
* current config and state.
|
||||
*/
|
||||
refreshVisibility() {
|
||||
|
||||
let config = this.Reveal.getConfig();
|
||||
|
||||
let slideNumberDisplay = 'none';
|
||||
if( config.slideNumber && !this.Reveal.isPrintingPDF() ) {
|
||||
if( config.showSlideNumber === 'all' ) {
|
||||
slideNumberDisplay = 'block';
|
||||
}
|
||||
else if( config.showSlideNumber === 'speaker' && this.Reveal.isSpeakerNotes() ) {
|
||||
slideNumberDisplay = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
this.element.style.display = slideNumberDisplay;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the slide number to match the current slide.
|
||||
*/
|
||||
update() {
|
||||
|
||||
// Update slide number if enabled
|
||||
if( this.Reveal.getConfig().slideNumber && this.element ) {
|
||||
this.element.innerHTML = this.getSlideNumber();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTML string corresponding to the current slide
|
||||
* number, including formatting.
|
||||
*/
|
||||
getSlideNumber( slide = this.Reveal.getCurrentSlide() ) {
|
||||
|
||||
let config = this.Reveal.getConfig();
|
||||
let value;
|
||||
let format = 'h.v';
|
||||
|
||||
if ( typeof config.slideNumber === 'function' ) {
|
||||
value = config.slideNumber( slide );
|
||||
} else {
|
||||
// Check if a custom number format is available
|
||||
if( typeof config.slideNumber === 'string' ) {
|
||||
format = config.slideNumber;
|
||||
}
|
||||
|
||||
// If there are ONLY vertical slides in this deck, always use
|
||||
// a flattened slide number
|
||||
if( !/c/.test( format ) && this.Reveal.getHorizontalSlides().length === 1 ) {
|
||||
format = 'c';
|
||||
}
|
||||
|
||||
value = [];
|
||||
switch( format ) {
|
||||
case 'c':
|
||||
value.push( this.Reveal.getSlidePastCount( slide ) + 1 );
|
||||
break;
|
||||
case 'c/t':
|
||||
value.push( this.Reveal.getSlidePastCount( slide ) + 1, '/', this.Reveal.getTotalSlides() );
|
||||
break;
|
||||
default:
|
||||
let indices = this.Reveal.getIndices( slide );
|
||||
value.push( indices.h + 1 );
|
||||
let sep = format === 'h/v' ? '/' : '.';
|
||||
if( this.Reveal.isVerticalSlide( slide ) ) value.push( sep, indices.v + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
let url = '#' + this.Reveal.location.getHash( slide );
|
||||
return this.formatNumber( value[0], value[1], value[2], url );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies HTML formatting to a slide number before it's
|
||||
* written to the DOM.
|
||||
*
|
||||
* @param {number} a Current slide
|
||||
* @param {string} delimiter Character to separate slide numbers
|
||||
* @param {(number|*)} b Total slides
|
||||
* @param {HTMLElement} [url='#'+locationHash()] The url to link to
|
||||
* @return {string} HTML string fragment
|
||||
*/
|
||||
formatNumber( a, delimiter, b, url = '#' + this.Reveal.location.getHash() ) {
|
||||
|
||||
if( typeof b === 'number' && !isNaN( b ) ) {
|
||||
return `<a href="${url}">
|
||||
<span class="slide-number-a">${a}</span>
|
||||
<span class="slide-number-delimiter">${delimiter}</span>
|
||||
<span class="slide-number-b">${b}</span>
|
||||
</a>`;
|
||||
}
|
||||
else {
|
||||
return `<a href="${url}">
|
||||
<span class="slide-number-a">${a}</span>
|
||||
</a>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
199
js/reveal.js
199
js/reveal.js
@ -1,8 +1,10 @@
|
||||
import SlideContent from './controllers/slidecontent.js'
|
||||
import SlideNumber from './controllers/slidenumber.js'
|
||||
import AutoAnimate from './controllers/autoanimate.js'
|
||||
import Fragments from './controllers/fragments.js'
|
||||
import Overview from './controllers/overview.js'
|
||||
import Keyboard from './controllers/keyboard.js'
|
||||
import Location from './controllers/location.js'
|
||||
import Plugins from './controllers/plugins.js'
|
||||
import Playback from './components/playback.js'
|
||||
import defaultConfig from './config.js'
|
||||
@ -18,6 +20,7 @@ import {
|
||||
distanceBetween,
|
||||
deserialize,
|
||||
transformElement,
|
||||
createSingletonNode,
|
||||
createStyleSheet,
|
||||
closestParent,
|
||||
enterFullscreen,
|
||||
@ -81,6 +84,9 @@ export default function( revealElement, options ) {
|
||||
// Controls loading and playback of slide content
|
||||
slideContent = new SlideContent( Reveal ),
|
||||
|
||||
// Controls the optional slide number display
|
||||
slideNumber = new SlideNumber( Reveal ),
|
||||
|
||||
// Controls auto-animations between slides
|
||||
autoAnimate = new AutoAnimate( Reveal ),
|
||||
|
||||
@ -93,6 +99,9 @@ export default function( revealElement, options ) {
|
||||
// Controls all keyboard interactions
|
||||
keyboard = new Keyboard( Reveal ),
|
||||
|
||||
// Controls the current location/URL
|
||||
location = new Location( Reveal ),
|
||||
|
||||
// List of asynchronously loaded reveal.js dependencies
|
||||
asyncDependencies = [],
|
||||
|
||||
@ -248,7 +257,7 @@ export default function( revealElement, options ) {
|
||||
<button class="navigate-down" aria-label="below slide"><div class="controls-arrow"></div></button>` );
|
||||
|
||||
// Slide number
|
||||
dom.slideNumber = createSingletonNode( dom.wrapper, 'div', 'slide-number', '' );
|
||||
slideNumber.createElement();
|
||||
|
||||
// Element containing notes that are visible to the audience
|
||||
dom.speakerNotes = createSingletonNode( dom.wrapper, 'div', 'speaker-notes', null );
|
||||
@ -377,7 +386,7 @@ export default function( revealElement, options ) {
|
||||
// Compute slide numbers now, before we start duplicating slides
|
||||
let doingSlideNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber );
|
||||
toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {
|
||||
slide.setAttribute( 'data-slide-number', getSlideNumber( slide ) );
|
||||
slide.setAttribute( 'data-slide-number', slideNumber.getSlideNumber( slide ) );
|
||||
} );
|
||||
|
||||
// Slide and slide background layout
|
||||
@ -534,42 +543,6 @@ export default function( revealElement, options ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an HTML element and returns a reference to it.
|
||||
* If the element already exists the existing instance will
|
||||
* be returned.
|
||||
*
|
||||
* @param {HTMLElement} container
|
||||
* @param {string} tagname
|
||||
* @param {string} classname
|
||||
* @param {string} innerHTML
|
||||
*
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
function createSingletonNode( container, tagname, classname, innerHTML='' ) {
|
||||
|
||||
// Find all nodes matching the description
|
||||
let nodes = container.querySelectorAll( '.' + classname );
|
||||
|
||||
// Check all matches to find one which is a direct child of
|
||||
// the specified container
|
||||
for( let i = 0; i < nodes.length; i++ ) {
|
||||
let testNode = nodes[i];
|
||||
if( testNode.parentNode === container ) {
|
||||
return testNode;
|
||||
}
|
||||
}
|
||||
|
||||
// If no node was found, create it now
|
||||
let node = document.createElement( tagname );
|
||||
node.className = classname;
|
||||
node.innerHTML = innerHTML;
|
||||
container.appendChild( node );
|
||||
|
||||
return node;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the slide background elements and appends them
|
||||
* to the background container. One element is created per
|
||||
@ -831,9 +804,8 @@ export default function( revealElement, options ) {
|
||||
|
||||
const numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length;
|
||||
|
||||
// Remove the previously configured transition class
|
||||
// The transition is added as a class on the .reveal element
|
||||
dom.wrapper.classList.remove( oldTransition );
|
||||
|
||||
dom.wrapper.classList.add( config.transition );
|
||||
|
||||
dom.wrapper.setAttribute( 'data-transition-speed', config.transitionSpeed );
|
||||
@ -927,19 +899,6 @@ export default function( revealElement, options ) {
|
||||
fragments.showAll();
|
||||
}
|
||||
|
||||
// Slide numbers
|
||||
let slideNumberDisplay = 'none';
|
||||
if( config.slideNumber && !isPrintingPDF() ) {
|
||||
if( config.showSlideNumber === 'all' ) {
|
||||
slideNumberDisplay = 'block';
|
||||
}
|
||||
else if( config.showSlideNumber === 'speaker' && isSpeakerNotes() ) {
|
||||
slideNumberDisplay = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
dom.slideNumber.style.display = slideNumberDisplay;
|
||||
|
||||
// Add the navigation mode to the DOM so we can adjust styling
|
||||
if( config.navigationMode !== 'default' ) {
|
||||
dom.wrapper.setAttribute( 'data-navigation-mode', config.navigationMode );
|
||||
@ -948,6 +907,7 @@ export default function( revealElement, options ) {
|
||||
dom.wrapper.removeAttribute( 'data-navigation-mode' );
|
||||
}
|
||||
|
||||
slideNumber.refreshVisibility();
|
||||
keyboard.refreshSortcuts();
|
||||
|
||||
sync();
|
||||
@ -1551,44 +1511,6 @@ export default function( revealElement, options ) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash URL that will resolve to the given slide location.
|
||||
*
|
||||
* @param {HTMLElement} [slide=currentSlide] The slide to link to
|
||||
*/
|
||||
function locationHash( slide ) {
|
||||
|
||||
let url = '/';
|
||||
|
||||
// Attempt to create a named link based on the slide's ID
|
||||
let s = slide || currentSlide;
|
||||
let id = s ? s.getAttribute( 'id' ) : null;
|
||||
if( id ) {
|
||||
id = encodeURIComponent( id );
|
||||
}
|
||||
|
||||
let index = getIndices( slide );
|
||||
if( !config.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 = config.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;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current or specified slide is vertical
|
||||
* (nested within another slide).
|
||||
@ -1908,9 +1830,9 @@ export default function( revealElement, options ) {
|
||||
updateProgress();
|
||||
updateBackground();
|
||||
updateParallax();
|
||||
updateSlideNumber();
|
||||
updateNotes();
|
||||
|
||||
slideNumber.update();
|
||||
fragments.update();
|
||||
|
||||
// Update the URL hash
|
||||
@ -1970,12 +1892,12 @@ export default function( revealElement, options ) {
|
||||
|
||||
updateControls();
|
||||
updateProgress();
|
||||
updateSlideNumber();
|
||||
updateSlidesVisibility();
|
||||
updateBackground( true );
|
||||
updateNotesVisibility();
|
||||
updateNotes();
|
||||
|
||||
slideNumber.update();
|
||||
slideContent.formatEmbeddedContent();
|
||||
|
||||
// Start or stop embedded content depending on global config
|
||||
@ -2322,90 +2244,6 @@ export default function( revealElement, options ) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the slide number to match the current slide.
|
||||
*/
|
||||
function updateSlideNumber() {
|
||||
|
||||
// Update slide number if enabled
|
||||
if( config.slideNumber && dom.slideNumber ) {
|
||||
dom.slideNumber.innerHTML = getSlideNumber();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTML string corresponding to the current slide number,
|
||||
* including formatting.
|
||||
*/
|
||||
function getSlideNumber( slide = currentSlide ) {
|
||||
|
||||
let value;
|
||||
let format = 'h.v';
|
||||
|
||||
if ( typeof config.slideNumber === 'function' ) {
|
||||
value = config.slideNumber( slide );
|
||||
} else {
|
||||
// Check if a custom number format is available
|
||||
if( typeof config.slideNumber === 'string' ) {
|
||||
format = config.slideNumber;
|
||||
}
|
||||
|
||||
// If there are ONLY vertical slides in this deck, always use
|
||||
// a flattened slide number
|
||||
if( !/c/.test( format ) && dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ).length === 1 ) {
|
||||
format = 'c';
|
||||
}
|
||||
|
||||
value = [];
|
||||
switch( format ) {
|
||||
case 'c':
|
||||
value.push( getSlidePastCount( slide ) + 1 );
|
||||
break;
|
||||
case 'c/t':
|
||||
value.push( getSlidePastCount( slide ) + 1, '/', getTotalSlides() );
|
||||
break;
|
||||
default:
|
||||
let indices = getIndices( slide );
|
||||
value.push( indices.h + 1 );
|
||||
let sep = format === 'h/v' ? '/' : '.';
|
||||
if( isVerticalSlide( slide ) ) value.push( sep, indices.v + 1 );
|
||||
}
|
||||
}
|
||||
|
||||
let url = '#' + locationHash( slide );
|
||||
return formatSlideNumber( value[0], value[1], value[2], url );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies HTML formatting to a slide number before it's
|
||||
* written to the DOM.
|
||||
*
|
||||
* @param {number} a Current slide
|
||||
* @param {string} delimiter Character to separate slide numbers
|
||||
* @param {(number|*)} b Total slides
|
||||
* @param {HTMLElement} [url='#'+locationHash()] The url to link to
|
||||
* @return {string} HTML string fragment
|
||||
*/
|
||||
function formatSlideNumber( a, delimiter, b, url = '#' + locationHash() ) {
|
||||
|
||||
if( typeof b === 'number' && !isNaN( b ) ) {
|
||||
return `<a href="${url}">
|
||||
<span class="slide-number-a">${a}</span>
|
||||
<span class="slide-number-delimiter">${delimiter}</span>
|
||||
<span class="slide-number-b">${b}</span>
|
||||
</a>`;
|
||||
}
|
||||
else {
|
||||
return `<a href="${url}">
|
||||
<span class="slide-number-a">${a}</span>
|
||||
</a>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the state of all control/navigation arrows.
|
||||
*/
|
||||
@ -2881,12 +2719,12 @@ export default function( revealElement, options ) {
|
||||
// If we're configured to push to history OR the history
|
||||
// API is not avaialble.
|
||||
if( config.history || !window.history ) {
|
||||
window.location.hash = locationHash();
|
||||
window.location.hash = location.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, '#' + locationHash() );
|
||||
window.history.replaceState( null, null, '#' + location.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
|
||||
@ -3810,12 +3648,14 @@ export default function( revealElement, options ) {
|
||||
isFirstSlide,
|
||||
isLastSlide,
|
||||
isLastVerticalSlide,
|
||||
isVerticalSlide,
|
||||
|
||||
// State checks
|
||||
isOverview: overview.isActive.bind( overview ),
|
||||
isPaused,
|
||||
isAutoSliding,
|
||||
isSpeakerNotes,
|
||||
isPrintingPDF,
|
||||
|
||||
// Slide preloading
|
||||
loadSlide: slideContent.load.bind( slideContent ),
|
||||
@ -3916,6 +3756,7 @@ export default function( revealElement, options ) {
|
||||
announceStatus,
|
||||
getStatusText,
|
||||
|
||||
location,
|
||||
overview,
|
||||
slideContent,
|
||||
onUserInput,
|
||||
|
@ -135,6 +135,42 @@ export const enterFullscreen = () => {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an HTML element and returns a reference to it.
|
||||
* If the element already exists the existing instance will
|
||||
* be returned.
|
||||
*
|
||||
* @param {HTMLElement} container
|
||||
* @param {string} tagname
|
||||
* @param {string} classname
|
||||
* @param {string} innerHTML
|
||||
*
|
||||
* @return {HTMLElement}
|
||||
*/
|
||||
export const createSingletonNode = ( container, tagname, classname, innerHTML='' ) => {
|
||||
|
||||
// Find all nodes matching the description
|
||||
let nodes = container.querySelectorAll( '.' + classname );
|
||||
|
||||
// Check all matches to find one which is a direct child of
|
||||
// the specified container
|
||||
for( let i = 0; i < nodes.length; i++ ) {
|
||||
let testNode = nodes[i];
|
||||
if( testNode.parentNode === container ) {
|
||||
return testNode;
|
||||
}
|
||||
}
|
||||
|
||||
// If no node was found, create it now
|
||||
let node = document.createElement( tagname );
|
||||
node.className = classname;
|
||||
node.innerHTML = innerHTML;
|
||||
container.appendChild( node );
|
||||
|
||||
return node;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the given CSS styles into the DOM.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user