|
@@ -3,7 +3,7 @@
|
|
|
* http://revealjs.com
|
|
|
* MIT licensed
|
|
|
*
|
|
|
- * Copyright (C) 2019 Hakim El Hattab, http://hakim.se
|
|
|
+ * Copyright (C) 2020 Hakim El Hattab, http://hakim.se
|
|
|
*/
|
|
|
(function( root, factory ) {
|
|
|
if( typeof define === 'function' && define.amd ) {
|
|
@@ -26,14 +26,18 @@
|
|
|
var Reveal;
|
|
|
|
|
|
// The reveal.js version
|
|
|
- var VERSION = '3.8.0';
|
|
|
+ var VERSION = '3.9.1';
|
|
|
|
|
|
var SLIDES_SELECTOR = '.slides section',
|
|
|
HORIZONTAL_SLIDES_SELECTOR = '.slides>section',
|
|
|
VERTICAL_SLIDES_SELECTOR = '.slides>section.present>section',
|
|
|
HOME_SLIDE_SELECTOR = '.slides>section:first-of-type',
|
|
|
+
|
|
|
UA = navigator.userAgent,
|
|
|
|
|
|
+ // Methods that may not be invoked via the postMessage API
|
|
|
+ POST_MESSAGE_METHOD_BLACKLIST = /registerPlugin|registerKeyboardShortcut|addKeyBinding|addEventListener/,
|
|
|
+
|
|
|
// Configuration defaults, can be overridden at initialization time
|
|
|
config = {
|
|
|
|
|
@@ -77,9 +81,9 @@
|
|
|
// - "c/t": Flattened slide number / total slides
|
|
|
//
|
|
|
// Alternatively, you can provide a function that returns the slide
|
|
|
- // number for the current slide. The function needs to return an array
|
|
|
- // with one string [slideNumber] or three strings [n1,delimiter,n2].
|
|
|
- // See #formatSlideNumber().
|
|
|
+ // number for the current slide. The function should take in a slide
|
|
|
+ // object and return an array with one string [slideNumber] or
|
|
|
+ // three strings [n1,delimiter,n2]. See #formatSlideNumber().
|
|
|
slideNumber: false,
|
|
|
|
|
|
// Can be used to limit the contexts in which the slide number appears
|
|
@@ -270,6 +274,11 @@
|
|
|
// Number of slides away from the current that are visible
|
|
|
viewDistance: 3,
|
|
|
|
|
|
+ // Number of slides away from the current that are visible on mobile
|
|
|
+ // devices. It is advisable to set this to a lower number than
|
|
|
+ // viewDistance in order to save resources.
|
|
|
+ mobileViewDistance: 2,
|
|
|
+
|
|
|
// The display mode that will be used to show slides
|
|
|
display: 'block',
|
|
|
|
|
@@ -447,7 +456,8 @@
|
|
|
*/
|
|
|
function checkCapabilities() {
|
|
|
|
|
|
- isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA );
|
|
|
+ isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA ) ||
|
|
|
+ ( navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 ); // iPadOS
|
|
|
isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA );
|
|
|
|
|
|
var testElement = document.createElement( 'div' );
|
|
@@ -849,17 +859,10 @@
|
|
|
// Make sure stretch elements fit on slide
|
|
|
layoutSlideContents( slideWidth, slideHeight );
|
|
|
|
|
|
- // Add each slide's index as attributes on itself, we need these
|
|
|
- // indices to generate slide numbers below
|
|
|
- toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) {
|
|
|
- hslide.setAttribute( 'data-index-h', h );
|
|
|
-
|
|
|
- if( hslide.classList.contains( 'stack' ) ) {
|
|
|
- toArray( hslide.querySelectorAll( 'section' ) ).forEach( function( vslide, v ) {
|
|
|
- vslide.setAttribute( 'data-index-h', h );
|
|
|
- vslide.setAttribute( 'data-index-v', v );
|
|
|
- } );
|
|
|
- }
|
|
|
+ // Compute slide numbers now, before we start duplicating slides
|
|
|
+ var 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 and slide background layout
|
|
@@ -930,14 +933,11 @@
|
|
|
}
|
|
|
|
|
|
// Inject slide numbers if `slideNumbers` are enabled
|
|
|
- if( config.slideNumber && /all|print/i.test( config.showSlideNumber ) ) {
|
|
|
- var slideNumberH = parseInt( slide.getAttribute( 'data-index-h' ), 10 ) + 1,
|
|
|
- slideNumberV = parseInt( slide.getAttribute( 'data-index-v' ), 10 ) + 1;
|
|
|
-
|
|
|
+ if( doingSlideNumbers ) {
|
|
|
var numberElement = document.createElement( 'div' );
|
|
|
numberElement.classList.add( 'slide-number' );
|
|
|
numberElement.classList.add( 'slide-number-pdf' );
|
|
|
- numberElement.innerHTML = formatSlideNumber( slideNumberH, '.', slideNumberV );
|
|
|
+ numberElement.innerHTML = slide.getAttribute( 'data-slide-number' );
|
|
|
page.appendChild( numberElement );
|
|
|
}
|
|
|
|
|
@@ -1278,11 +1278,20 @@
|
|
|
|
|
|
// Check if the requested method can be found
|
|
|
if( data.method && typeof Reveal[data.method] === 'function' ) {
|
|
|
- var result = Reveal[data.method].apply( Reveal, data.args );
|
|
|
|
|
|
- // Dispatch a postMessage event with the returned value from
|
|
|
- // our method invocation for getter functions
|
|
|
- dispatchPostMessage( 'callback', { method: data.method, result: result } );
|
|
|
+ if( POST_MESSAGE_METHOD_BLACKLIST.test( data.method ) === false ) {
|
|
|
+
|
|
|
+ var result = Reveal[data.method].apply( Reveal, data.args );
|
|
|
+
|
|
|
+ // Dispatch a postMessage event with the returned value from
|
|
|
+ // our method invocation for getter functions
|
|
|
+ dispatchPostMessage( 'callback', { method: data.method, result: result } );
|
|
|
+
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ console.warn( 'reveal.js: "'+ data.method +'" is is blacklisted from the postMessage API' );
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
}, false );
|
|
@@ -2651,34 +2660,37 @@
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Return a hash URL that will resolve to the current slide location.
|
|
|
+ * Return a hash URL that will resolve to the given slide location.
|
|
|
+ *
|
|
|
+ * @param {HTMLElement} [slide=currentSlide] The slide to link to
|
|
|
*/
|
|
|
- function locationHash() {
|
|
|
+ function locationHash( slide ) {
|
|
|
|
|
|
var url = '/';
|
|
|
|
|
|
// Attempt to create a named link based on the slide's ID
|
|
|
- var id = currentSlide ? currentSlide.getAttribute( 'id' ) : null;
|
|
|
+ var s = slide || currentSlide;
|
|
|
+ var id = s ? s.getAttribute( 'id' ) : null;
|
|
|
if( id ) {
|
|
|
id = encodeURIComponent( id );
|
|
|
}
|
|
|
|
|
|
- var indexf;
|
|
|
- if( config.fragmentInURL ) {
|
|
|
- indexf = getIndices().f;
|
|
|
+ var 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 && indexf === undefined ) {
|
|
|
+ if( typeof id === 'string' && id.length && index.f === undefined ) {
|
|
|
url = '/' + id;
|
|
|
}
|
|
|
// Otherwise use the /h/v index
|
|
|
else {
|
|
|
var hashIndexBase = config.hashOneBasedIndex ? 1 : 0;
|
|
|
- if( indexh > 0 || indexv > 0 || indexf !== undefined ) url += indexh + hashIndexBase;
|
|
|
- if( indexv > 0 || indexf !== undefined ) url += '/' + (indexv + hashIndexBase );
|
|
|
- if( indexf !== undefined ) url += '/' + indexf;
|
|
|
+ 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;
|
|
@@ -3283,9 +3295,10 @@
|
|
|
// be visible
|
|
|
var viewDistance = isOverview() ? 10 : config.viewDistance;
|
|
|
|
|
|
- // Limit view distance on weaker devices
|
|
|
+ // Shorten the view distance on devices that typically have
|
|
|
+ // less resources
|
|
|
if( isMobileDevice ) {
|
|
|
- viewDistance = isOverview() ? 6 : 2;
|
|
|
+ viewDistance = isOverview() ? 6 : config.mobileViewDistance;
|
|
|
}
|
|
|
|
|
|
// All slides need to be visible when exporting to PDF
|
|
@@ -3421,46 +3434,56 @@
|
|
|
|
|
|
// Update slide number if enabled
|
|
|
if( config.slideNumber && dom.slideNumber ) {
|
|
|
+ dom.slideNumber.innerHTML = getSlideNumber();
|
|
|
+ }
|
|
|
|
|
|
- var value;
|
|
|
- var format = 'h.v';
|
|
|
+ }
|
|
|
|
|
|
- if( typeof config.slideNumber === 'function' ) {
|
|
|
- value = config.slideNumber();
|
|
|
- }
|
|
|
- else {
|
|
|
- // Check if a custom number format is available
|
|
|
- if( typeof config.slideNumber === 'string' ) {
|
|
|
- format = config.slideNumber;
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Returns the HTML string corresponding to the current slide number,
|
|
|
+ * including formatting.
|
|
|
+ */
|
|
|
+ function getSlideNumber( slide ) {
|
|
|
|
|
|
- // 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';
|
|
|
- }
|
|
|
+ var value;
|
|
|
+ var format = 'h.v';
|
|
|
+ if( slide === undefined ) {
|
|
|
+ slide = currentSlide;
|
|
|
+ }
|
|
|
|
|
|
- value = [];
|
|
|
- switch( format ) {
|
|
|
- case 'c':
|
|
|
- value.push( getSlidePastCount() + 1 );
|
|
|
- break;
|
|
|
- case 'c/t':
|
|
|
- value.push( getSlidePastCount() + 1, '/', getTotalSlides() );
|
|
|
- break;
|
|
|
- case 'h/v':
|
|
|
- value.push( indexh + 1 );
|
|
|
- if( isVerticalSlide() ) value.push( '/', indexv + 1 );
|
|
|
- break;
|
|
|
- default:
|
|
|
- value.push( indexh + 1 );
|
|
|
- if( isVerticalSlide() ) value.push( '.', indexv + 1 );
|
|
|
- }
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
- dom.slideNumber.innerHTML = formatSlideNumber( value[0], value[1], value[2] );
|
|
|
+ // 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:
|
|
|
+ var indices = getIndices( slide );
|
|
|
+ value.push( indices.h + 1 );
|
|
|
+ var sep = format === 'h/v' ? '/' : '.';
|
|
|
+ if( isVerticalSlide( slide ) ) value.push( sep, indices.v + 1 );
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ var url = '#' + locationHash( slide );
|
|
|
+ return formatSlideNumber( value[0], value[1], value[2], url );
|
|
|
+
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -3470,11 +3493,14 @@
|
|
|
* @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 ) {
|
|
|
+ function formatSlideNumber( a, delimiter, b, url ) {
|
|
|
|
|
|
- var url = '#' + locationHash();
|
|
|
+ if( url === undefined ) {
|
|
|
+ url = '#' + locationHash();
|
|
|
+ }
|
|
|
if( typeof b === 'number' && !isNaN( b ) ) {
|
|
|
return '<a href="' + url + '">' +
|
|
|
'<span class="slide-number-a">'+ a +'</span>' +
|
|
@@ -3855,6 +3881,7 @@
|
|
|
iframe.setAttribute( 'allowfullscreen', '' );
|
|
|
iframe.setAttribute( 'mozallowfullscreen', '' );
|
|
|
iframe.setAttribute( 'webkitallowfullscreen', '' );
|
|
|
+ iframe.setAttribute( 'allow', 'autoplay' );
|
|
|
|
|
|
iframe.setAttribute( 'data-src', backgroundIframe );
|
|
|
|
|
@@ -4224,9 +4251,15 @@
|
|
|
* Returns the number of past slides. This can be used as a global
|
|
|
* flattened index for slides.
|
|
|
*
|
|
|
+ * @param {HTMLElement} [slide=currentSlide] The slide we're counting before
|
|
|
+ *
|
|
|
* @return {number} Past slide count
|
|
|
*/
|
|
|
- function getSlidePastCount() {
|
|
|
+ function getSlidePastCount( slide ) {
|
|
|
+
|
|
|
+ if( slide === undefined ) {
|
|
|
+ slide = currentSlide;
|
|
|
+ }
|
|
|
|
|
|
var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
|
|
|
|
|
@@ -4242,7 +4275,7 @@
|
|
|
for( var j = 0; j < verticalSlides.length; j++ ) {
|
|
|
|
|
|
// Stop as soon as we arrive at the present
|
|
|
- if( verticalSlides[j].classList.contains( 'present' ) ) {
|
|
|
+ if( verticalSlides[j] === slide ) {
|
|
|
break mainLoop;
|
|
|
}
|
|
|
|
|
@@ -4251,7 +4284,7 @@
|
|
|
}
|
|
|
|
|
|
// Stop as soon as we arrive at the present
|
|
|
- if( horizontalSlide.classList.contains( 'present' ) ) {
|
|
|
+ if( horizontalSlide === slide ) {
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -4743,6 +4776,8 @@
|
|
|
|
|
|
if( fragments.length ) {
|
|
|
|
|
|
+ var maxIndex = 0;
|
|
|
+
|
|
|
if( typeof index !== 'number' ) {
|
|
|
var currentFragment = sortFragments( currentSlide.querySelectorAll( '.fragment.visible' ) ).pop();
|
|
|
if( currentFragment ) {
|
|
@@ -4756,6 +4791,8 @@
|
|
|
i = parseInt( el.getAttribute( 'data-fragment-index' ), 10 );
|
|
|
}
|
|
|
|
|
|
+ maxIndex = Math.max( maxIndex, i );
|
|
|
+
|
|
|
// Visible fragments
|
|
|
if( i <= index ) {
|
|
|
if( !el.classList.contains( 'visible' ) ) changedFragments.shown.push( el );
|
|
@@ -4779,6 +4816,13 @@
|
|
|
|
|
|
} );
|
|
|
|
|
|
+ // Write the current fragment index to the slide <section>.
|
|
|
+ // This can be used by end users to apply styles based on
|
|
|
+ // the current fragment index.
|
|
|
+ index = typeof index === 'number' ? index : -1;
|
|
|
+ index = Math.max( Math.min( index, maxIndex ), -1 );
|
|
|
+ currentSlide.setAttribute( 'data-fragment', index );
|
|
|
+
|
|
|
}
|
|
|
|
|
|
}
|
|
@@ -5436,19 +5480,49 @@
|
|
|
|
|
|
if( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
|
|
|
touch.captured = true;
|
|
|
- navigateLeft();
|
|
|
+ if( config.navigationMode === 'linear' ) {
|
|
|
+ if( config.rtl ) {
|
|
|
+ navigateNext();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ navigatePrev();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ navigateLeft();
|
|
|
+ }
|
|
|
}
|
|
|
else if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
|
|
|
touch.captured = true;
|
|
|
- navigateRight();
|
|
|
+ if( config.navigationMode === 'linear' ) {
|
|
|
+ if( config.rtl ) {
|
|
|
+ navigatePrev();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ navigateNext();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ navigateRight();
|
|
|
+ }
|
|
|
}
|
|
|
else if( deltaY > touch.threshold ) {
|
|
|
touch.captured = true;
|
|
|
- navigateUp();
|
|
|
+ if( config.navigationMode === 'linear' ) {
|
|
|
+ navigatePrev();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ navigateUp();
|
|
|
+ }
|
|
|
}
|
|
|
else if( deltaY < -touch.threshold ) {
|
|
|
touch.captured = true;
|
|
|
- navigateDown();
|
|
|
+ if( config.navigationMode === 'linear' ) {
|
|
|
+ navigateNext();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ navigateDown();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// If we're embedded, only block touch events if they have
|