瀏覽代碼

add slidenumber & location controllers

Hakim El Hattab 5 年之前
父節點
當前提交
3683ad255d
共有 6 個文件被更改,包括 239 次插入183 次删除
  1. 0 0
      dist/reveal.min.js
  2. 4 4
      js/controllers/keyboard.js
  3. 51 0
      js/controllers/location.js
  4. 128 0
      js/controllers/slidenumber.js
  5. 20 179
      js/reveal.js
  6. 36 0
      js/utils/util.js

文件差異過大導致無法顯示
+ 0 - 0
dist/reveal.min.js


+ 4 - 4
js/controllers/keyboard.js

@@ -263,7 +263,7 @@ export default class Keyboard {
 			}
 			}
 			// N, PAGE DOWN
 			// N, PAGE DOWN
 			else if( keyCode === 78 || keyCode === 34 ) {
 			else if( keyCode === 78 || keyCode === 34 ) {
-				this.Review.next();
+				this.Reveal.next();
 			}
 			}
 			// H, LEFT
 			// H, LEFT
 			else if( keyCode === 72 || keyCode === 37 ) {
 			else if( keyCode === 72 || keyCode === 37 ) {
@@ -283,7 +283,7 @@ export default class Keyboard {
 					this.Reveal.slide( Number.MAX_VALUE );
 					this.Reveal.slide( Number.MAX_VALUE );
 				}
 				}
 				else if( !this.Reveal.overview.isActive() && useLinearMode ) {
 				else if( !this.Reveal.overview.isActive() && useLinearMode ) {
-					this.Review.next();
+					this.Reveal.next();
 				}
 				}
 				else {
 				else {
 					this.Reveal.right();
 					this.Reveal.right();
@@ -301,7 +301,7 @@ export default class Keyboard {
 			// J, DOWN
 			// J, DOWN
 			else if( keyCode === 74 || keyCode === 40 ) {
 			else if( keyCode === 74 || keyCode === 40 ) {
 				if( !this.Reveal.overview.isActive() && useLinearMode ) {
 				if( !this.Reveal.overview.isActive() && useLinearMode ) {
-					this.Review.next();
+					this.Reveal.next();
 				}
 				}
 				else {
 				else {
 					this.Reveal.down();
 					this.Reveal.down();
@@ -324,7 +324,7 @@ export default class Keyboard {
 					this.Reveal.prev();
 					this.Reveal.prev();
 				}
 				}
 				else {
 				else {
-					this.Review.next();
+					this.Reveal.next();
 				}
 				}
 			}
 			}
 			// TWO-SPOT, SEMICOLON, B, V, PERIOD, LOGITECH PRESENTER TOOLS "BLACK SCREEN" BUTTON
 			// TWO-SPOT, SEMICOLON, B, V, PERIOD, LOGITECH PRESENTER TOOLS "BLACK SCREEN" BUTTON

+ 51 - 0
js/controllers/location.js

@@ -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 - 0
js/controllers/slidenumber.js

@@ -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>`;
+		}
+
+	}
+
+}

+ 20 - 179
js/reveal.js

@@ -1,8 +1,10 @@
 import SlideContent from './controllers/slidecontent.js'
 import SlideContent from './controllers/slidecontent.js'
+import SlideNumber from './controllers/slidenumber.js'
 import AutoAnimate from './controllers/autoanimate.js'
 import AutoAnimate from './controllers/autoanimate.js'
 import Fragments from './controllers/fragments.js'
 import Fragments from './controllers/fragments.js'
 import Overview from './controllers/overview.js'
 import Overview from './controllers/overview.js'
 import Keyboard from './controllers/keyboard.js'
 import Keyboard from './controllers/keyboard.js'
+import Location from './controllers/location.js'
 import Plugins from './controllers/plugins.js'
 import Plugins from './controllers/plugins.js'
 import Playback from './components/playback.js'
 import Playback from './components/playback.js'
 import defaultConfig from './config.js'
 import defaultConfig from './config.js'
@@ -18,6 +20,7 @@ import {
 	distanceBetween,
 	distanceBetween,
 	deserialize,
 	deserialize,
 	transformElement,
 	transformElement,
+	createSingletonNode,
 	createStyleSheet,
 	createStyleSheet,
 	closestParent,
 	closestParent,
 	enterFullscreen,
 	enterFullscreen,
@@ -81,6 +84,9 @@ export default function( revealElement, options ) {
 		// Controls loading and playback of slide content
 		// Controls loading and playback of slide content
 		slideContent = new SlideContent( Reveal ),
 		slideContent = new SlideContent( Reveal ),
 
 
+		// Controls the optional slide number display
+		slideNumber = new SlideNumber( Reveal ),
+
 		// Controls auto-animations between slides
 		// Controls auto-animations between slides
 		autoAnimate = new AutoAnimate( Reveal ),
 		autoAnimate = new AutoAnimate( Reveal ),
 
 
@@ -93,6 +99,9 @@ export default function( revealElement, options ) {
 		// Controls all keyboard interactions
 		// Controls all keyboard interactions
 		keyboard = new Keyboard( Reveal ),
 		keyboard = new Keyboard( Reveal ),
 
 
+		// Controls the current location/URL
+		location = new Location( Reveal ),
+
 		// List of asynchronously loaded reveal.js dependencies
 		// List of asynchronously loaded reveal.js dependencies
 		asyncDependencies = [],
 		asyncDependencies = [],
 
 
@@ -248,7 +257,7 @@ export default function( revealElement, options ) {
 			<button class="navigate-down" aria-label="below slide"><div class="controls-arrow"></div></button>` );
 			<button class="navigate-down" aria-label="below slide"><div class="controls-arrow"></div></button>` );
 
 
 		// Slide number
 		// Slide number
-		dom.slideNumber = createSingletonNode( dom.wrapper, 'div', 'slide-number', '' );
+		slideNumber.createElement();
 
 
 		// Element containing notes that are visible to the audience
 		// Element containing notes that are visible to the audience
 		dom.speakerNotes = createSingletonNode( dom.wrapper, 'div', 'speaker-notes', null );
 		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
 		// Compute slide numbers now, before we start duplicating slides
 		let doingSlideNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber );
 		let doingSlideNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber );
 		toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {
 		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
 		// 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
 	 * Creates the slide background elements and appends them
 	 * to the background container. One element is created per
 	 * 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;
 		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.remove( oldTransition );
-
 		dom.wrapper.classList.add( config.transition );
 		dom.wrapper.classList.add( config.transition );
 
 
 		dom.wrapper.setAttribute( 'data-transition-speed', config.transitionSpeed );
 		dom.wrapper.setAttribute( 'data-transition-speed', config.transitionSpeed );
@@ -927,19 +899,6 @@ export default function( revealElement, options ) {
 			fragments.showAll();
 			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
 		// Add the navigation mode to the DOM so we can adjust styling
 		if( config.navigationMode !== 'default' ) {
 		if( config.navigationMode !== 'default' ) {
 			dom.wrapper.setAttribute( 'data-navigation-mode', config.navigationMode );
 			dom.wrapper.setAttribute( 'data-navigation-mode', config.navigationMode );
@@ -948,6 +907,7 @@ export default function( revealElement, options ) {
 			dom.wrapper.removeAttribute( 'data-navigation-mode' );
 			dom.wrapper.removeAttribute( 'data-navigation-mode' );
 		}
 		}
 
 
+		slideNumber.refreshVisibility();
 		keyboard.refreshSortcuts();
 		keyboard.refreshSortcuts();
 
 
 		sync();
 		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
 	 * Checks if the current or specified slide is vertical
 	 * (nested within another slide).
 	 * (nested within another slide).
@@ -1908,9 +1830,9 @@ export default function( revealElement, options ) {
 		updateProgress();
 		updateProgress();
 		updateBackground();
 		updateBackground();
 		updateParallax();
 		updateParallax();
-		updateSlideNumber();
 		updateNotes();
 		updateNotes();
 
 
+		slideNumber.update();
 		fragments.update();
 		fragments.update();
 
 
 		// Update the URL hash
 		// Update the URL hash
@@ -1970,12 +1892,12 @@ export default function( revealElement, options ) {
 
 
 		updateControls();
 		updateControls();
 		updateProgress();
 		updateProgress();
-		updateSlideNumber();
 		updateSlidesVisibility();
 		updateSlidesVisibility();
 		updateBackground( true );
 		updateBackground( true );
 		updateNotesVisibility();
 		updateNotesVisibility();
 		updateNotes();
 		updateNotes();
 
 
+		slideNumber.update();
 		slideContent.formatEmbeddedContent();
 		slideContent.formatEmbeddedContent();
 
 
 		// Start or stop embedded content depending on global config
 		// 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.
 	 * 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
 			// If we're configured to push to history OR the history
 			// API is not avaialble.
 			// API is not avaialble.
 			if( config.history || !window.history ) {
 			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
 			// If we're configured to reflect the current slide in the
 			// URL without pushing to history.
 			// URL without pushing to history.
 			else if( config.hash ) {
 			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
 			// If history and hash are both disabled, a hash may still
 			// be added to the URL by clicking on a href with a hash
 			// be added to the URL by clicking on a href with a hash
@@ -3810,12 +3648,14 @@ export default function( revealElement, options ) {
 		isFirstSlide,
 		isFirstSlide,
 		isLastSlide,
 		isLastSlide,
 		isLastVerticalSlide,
 		isLastVerticalSlide,
+		isVerticalSlide,
 
 
 		// State checks
 		// State checks
 		isOverview: overview.isActive.bind( overview ),
 		isOverview: overview.isActive.bind( overview ),
 		isPaused,
 		isPaused,
 		isAutoSliding,
 		isAutoSliding,
 		isSpeakerNotes,
 		isSpeakerNotes,
+		isPrintingPDF,
 
 
 		// Slide preloading
 		// Slide preloading
 		loadSlide: slideContent.load.bind( slideContent ),
 		loadSlide: slideContent.load.bind( slideContent ),
@@ -3916,6 +3756,7 @@ export default function( revealElement, options ) {
 		announceStatus,
 		announceStatus,
 		getStatusText,
 		getStatusText,
 
 
+		location,
 		overview,
 		overview,
 		slideContent,
 		slideContent,
 		onUserInput,
 		onUserInput,

+ 36 - 0
js/utils/util.js

@@ -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.
  * Injects the given CSS styles into the DOM.
  *
  *

部分文件因文件數量過多而無法顯示