diff options
Diffstat (limited to 'js')
-rw-r--r-- | js/reveal.js | 396 |
1 files changed, 226 insertions, 170 deletions
diff --git a/js/reveal.js b/js/reveal.js index d2b2970..e544e13 100644 --- a/js/reveal.js +++ b/js/reveal.js @@ -3,7 +3,7 @@ * http://lab.hakim.se/reveal-js * MIT licensed * - * Copyright (C) 2015 Hakim El Hattab, http://hakim.se + * Copyright (C) 2016 Hakim El Hattab, http://hakim.se */ (function( root, factory ) { if( typeof define === 'function' && define.amd ) { @@ -25,10 +25,14 @@ var Reveal; + // The reveal.js version + var VERSION = '3.3.0'; + 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, // Configuration defaults, can be overridden at initialization time config = { @@ -39,11 +43,11 @@ height: 700, // Factor of the display size that should remain empty around the content - margin: 0.1, + margin: 0.04, // Bounds for smallest/largest possible scale to apply to content minScale: 0.2, - maxScale: 1.5, + maxScale: 2.0, // Display controls in the bottom right corner controls: true, @@ -78,6 +82,9 @@ // Change the presentation direction to be RTL rtl: false, + // Randomizes the order of slides each time the presentation loads + shuffle: false, + // Turns fragments on and off globally fragments: true, @@ -103,6 +110,9 @@ // Stop auto-sliding after user input autoSlideStoppable: true, + // Use this method for navigation when auto-sliding (defaults to navigateNext) + autoSlideMethod: null, + // Enable slide navigation via mouse wheel mouseWheel: false, @@ -143,6 +153,10 @@ parallaxBackgroundHorizontal: null, parallaxBackgroundVertical: null, + // The maximum number of pages a single slide can expand onto when printing + // to PDF, unlimited by default + pdfMaxPagesPerSlide: Number.POSITIVE_INFINITY, + // Number of slides away from the current that are visible viewDistance: 3, @@ -151,12 +165,19 @@ }, + // Flags if Reveal.initialize() has been called + initialized = false, + // Flags if reveal.js is loaded (has dispatched the 'ready' event) loaded = false, // Flags if the overview mode is currently active overview = false, + // Holds the dimensions of our overview slides, including margins + overviewSlideWidth = null, + overviewSlideHeight = null, + // The horizontal and vertical index of the currently active slide indexh, indexv, @@ -188,6 +209,9 @@ // Client is a mobile device, see #checkCapabilities() isMobileDevice, + // Client is a desktop Chrome, see #checkCapabilities() + isChrome, + // Throttles mouse wheel navigation lastMouseWheelStep = 0, @@ -236,6 +260,11 @@ */ function initialize( options ) { + // Make sure we only initialize once + if( initialized === true ) return; + + initialized = true; + checkCapabilities(); if( !features.transforms2d && !features.transforms3d ) { @@ -292,30 +321,37 @@ */ function checkCapabilities() { - features.transforms3d = 'WebkitPerspective' in document.body.style || - 'MozPerspective' in document.body.style || - 'msPerspective' in document.body.style || - 'OPerspective' in document.body.style || - 'perspective' in document.body.style; + isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA ); + isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA ); + + var testElement = document.createElement( 'div' ); - features.transforms2d = 'WebkitTransform' in document.body.style || - 'MozTransform' in document.body.style || - 'msTransform' in document.body.style || - 'OTransform' in document.body.style || - 'transform' in document.body.style; + features.transforms3d = 'WebkitPerspective' in testElement.style || + 'MozPerspective' in testElement.style || + 'msPerspective' in testElement.style || + 'OPerspective' in testElement.style || + 'perspective' in testElement.style; + + features.transforms2d = 'WebkitTransform' in testElement.style || + 'MozTransform' in testElement.style || + 'msTransform' in testElement.style || + 'OTransform' in testElement.style || + 'transform' in testElement.style; features.requestAnimationFrameMethod = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame; features.requestAnimationFrame = typeof features.requestAnimationFrameMethod === 'function'; features.canvas = !!document.createElement( 'canvas' ).getContext; - features.touch = !!( 'ontouchstart' in window ); - // Transitions in the overview are disabled in desktop and - // mobile Safari due to lag - features.overviewTransitions = !/Version\/[\d\.]+.*Safari/.test( navigator.userAgent ); + // Safari due to lag + features.overviewTransitions = !/Version\/[\d\.]+.*Safari/.test( UA ); - isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( navigator.userAgent ); + // Flags if we should use zoom instead of transform to scale + // up slides. Zoom produces crisper results but has a lot of + // xbrowser quirks so we only use it in whitelsited browsers. + features.zoom = 'zoom' in testElement.style && !isMobileDevice && + ( isChrome || /Version\/[\d\.]+.*Safari/.test( UA ) ); } @@ -395,8 +431,8 @@ // Listen to messages posted to this window setupPostMessage(); - // Prevent iframes from scrolling the slides out of view - setupIframeScrollPrevention(); + // Prevent the slides from being scrolled out of view + setupScrollPrevention(); // Resets all vertical slides so that only the first is visible resetVerticalSlides(); @@ -471,6 +507,7 @@ // Element containing notes that are visible to the audience dom.speakerNotes = createSingletonNode( dom.wrapper, 'div', 'speaker-notes', null ); dom.speakerNotes.setAttribute( 'data-prevent-swipe', '' ); + dom.speakerNotes.setAttribute( 'tabindex', '0' ); // Overlay graphic which is displayed during the paused mode createSingletonNode( dom.wrapper, 'div', 'pause-overlay', null ); @@ -565,43 +602,61 @@ var left = ( pageWidth - slideWidth ) / 2, top = ( pageHeight - slideHeight ) / 2; - var contentHeight = getAbsoluteHeight( slide ); + var contentHeight = slide.scrollHeight; var numberOfPages = Math.max( Math.ceil( contentHeight / pageHeight ), 1 ); + // Adhere to configured pages per slide limit + numberOfPages = Math.min( numberOfPages, config.pdfMaxPagesPerSlide ); + // Center slides vertically if( numberOfPages === 1 && config.center || slide.classList.contains( 'center' ) ) { top = Math.max( ( pageHeight - contentHeight ) / 2, 0 ); } + // Wrap the slide in a page element and hide its overflow + // so that no page ever flows onto another + var page = document.createElement( 'div' ); + page.className = 'pdf-page'; + page.style.height = ( pageHeight * numberOfPages ) + 'px'; + slide.parentNode.insertBefore( page, slide ); + page.appendChild( slide ); + // Position the slide inside of the page slide.style.left = left + 'px'; slide.style.top = top + 'px'; slide.style.width = slideWidth + 'px'; - // TODO Backgrounds need to be multiplied when the slide - // stretches over multiple pages - var background = slide.querySelector( '.slide-background' ); - if( background ) { - background.style.width = pageWidth + 'px'; - background.style.height = ( pageHeight * numberOfPages ) + 'px'; - background.style.top = -top + 'px'; - background.style.left = -left + 'px'; + if( slide.slideBackgroundElement ) { + page.insertBefore( slide.slideBackgroundElement, slide ); } // Inject notes if `showNotes` is enabled if( config.showNotes ) { + + // Are there notes for this slide? var notes = getSlideNotes( slide ); if( notes ) { + var notesSpacing = 8; + var notesLayout = typeof config.showNotes === 'string' ? config.showNotes : 'inline'; var notesElement = document.createElement( 'div' ); notesElement.classList.add( 'speaker-notes' ); notesElement.classList.add( 'speaker-notes-pdf' ); + notesElement.setAttribute( 'data-layout', notesLayout ); notesElement.innerHTML = notes; - notesElement.style.left = ( notesSpacing - left ) + 'px'; - notesElement.style.bottom = ( notesSpacing - top ) + 'px'; - notesElement.style.width = ( pageWidth - notesSpacing*2 ) + 'px'; - slide.appendChild( notesElement ); + + if( notesLayout === 'separate-page' ) { + page.parentNode.insertBefore( notesElement, page.nextSibling ); + } + else { + notesElement.style.left = notesSpacing + 'px'; + notesElement.style.bottom = notesSpacing + 'px'; + notesElement.style.width = ( pageWidth - notesSpacing*2 ) + 'px'; + page.appendChild( notesElement ); + } + } + } // Inject slide numbers if `slideNumbers` are enabled @@ -613,7 +668,7 @@ numberElement.classList.add( 'slide-number' ); numberElement.classList.add( 'slide-number-pdf' ); numberElement.innerHTML = formatSlideNumber( slideNumberH, '.', slideNumberV ); - background.appendChild( numberElement ); + page.appendChild( numberElement ); } } @@ -627,22 +682,22 @@ } /** - * This is an unfortunate necessity. Iframes can trigger the - * parent window to scroll, for example by focusing an input. + * This is an unfortunate necessity. Some actions – such as + * an input field being focused in an iframe or using the + * keyboard to expand text selection beyond the bounds of + * a slide – can trigger our content to be pushed out of view. * This scrolling can not be prevented by hiding overflow in - * CSS so we have to resort to repeatedly checking if the - * browser has decided to offset our slides :( + * CSS (we already do) so we have to resort to repeatedly + * checking if the slides have been offset :( */ - function setupIframeScrollPrevention() { + function setupScrollPrevention() { - if( dom.slides.querySelector( 'iframe' ) ) { - setInterval( function() { - if( dom.wrapper.scrollTop !== 0 || dom.wrapper.scrollLeft !== 0 ) { - dom.wrapper.scrollTop = 0; - dom.wrapper.scrollLeft = 0; - } - }, 500 ); - } + setInterval( function() { + if( dom.wrapper.scrollTop !== 0 || dom.wrapper.scrollLeft !== 0 ) { + dom.wrapper.scrollTop = 0; + dom.wrapper.scrollLeft = 0; + } + }, 1000 ); } @@ -693,24 +748,12 @@ // Iterate over all horizontal slides toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( slideh ) { - var backgroundStack; - - if( printMode ) { - backgroundStack = createBackground( slideh, slideh ); - } - else { - backgroundStack = createBackground( slideh, dom.background ); - } + var backgroundStack = createBackground( slideh, dom.background ); // Iterate over all vertical slides toArray( slideh.querySelectorAll( 'section' ) ).forEach( function( slidev ) { - if( printMode ) { - createBackground( slidev, slidev ); - } - else { - createBackground( slidev, backgroundStack ); - } + createBackground( slidev, backgroundStack ); backgroundStack.classList.add( 'stack' ); @@ -806,6 +849,8 @@ slide.classList.remove( 'has-dark-background' ); slide.classList.remove( 'has-light-background' ); + slide.slideBackgroundElement = element; + // If this slide has a background color, add a class that // signals if it is light or dark. If the slide has no background // color, no class will be set @@ -886,6 +931,10 @@ dom.progress.style.display = config.progress ? 'block' : 'none'; dom.slideNumber.style.display = config.slideNumber && !isPrintingPDF() ? 'block' : 'none'; + if( config.shuffle ) { + shuffle(); + } + if( config.rtl ) { dom.wrapper.classList.add( 'rtl' ); } @@ -907,6 +956,7 @@ if( config.showNotes ) { dom.speakerNotes.classList.add( 'visible' ); + dom.speakerNotes.setAttribute( 'data-layout', typeof config.showNotes === 'string' ? config.showNotes : 'inline' ); } else { dom.speakerNotes.classList.remove( 'visible' ); @@ -1029,7 +1079,7 @@ // Only support touch for Android, fixes double navigations in // stock browser - if( navigator.userAgent.match( /android/gi ) ) { + if( UA.match( /android/gi ) ) { pointerEvents = [ 'touchstart' ]; } @@ -1263,41 +1313,6 @@ } /** - * Retrieves the height of the given element by looking - * at the position and height of its immediate children. - */ - function getAbsoluteHeight( element ) { - - var height = 0; - - if( element ) { - var absoluteChildren = 0; - - toArray( element.childNodes ).forEach( function( child ) { - - if( typeof child.offsetTop === 'number' && child.style ) { - // Count # of abs children - if( window.getComputedStyle( child ).position === 'absolute' ) { - absoluteChildren += 1; - } - - height = Math.max( height, child.offsetTop + child.offsetHeight ); - } - - } ); - - // If there are no absolute children, use offsetHeight - if( absoluteChildren === 0 ) { - height = element.offsetHeight; - } - - } - - return height; - - } - - /** * Returns the remaining height within the parent of the * target element. * @@ -1561,10 +1576,8 @@ var size = getComputedSlideSize(); - var slidePadding = 20; // TODO Dig this out of DOM - // Layout the contents of the slides - layoutSlideContents( config.width, config.height, slidePadding ); + layoutSlideContents( config.width, config.height ); dom.slides.style.width = size.width + 'px'; dom.slides.style.height = size.height + 'px'; @@ -1586,10 +1599,10 @@ transformSlides( { layout: '' } ); } else { - // Use zoom to scale up in desktop Chrome so that content - // remains crisp. We don't use zoom to scale down since that - // can lead to shifts in text layout/line breaks. - if( scale > 1 && !isMobileDevice && /chrome/i.test( navigator.userAgent ) && typeof dom.slides.style.zoom !== 'undefined' ) { + // Prefer zoom for scaling up so that content remains crisp. + // Don't use zoom to scale down since that can lead to shifts + // in text layout/line breaks. + if( scale > 1 && features.zoom ) { dom.slides.style.zoom = scale; dom.slides.style.left = ''; dom.slides.style.top = ''; @@ -1626,7 +1639,7 @@ slide.style.top = 0; } else { - slide.style.top = Math.max( ( ( size.height - getAbsoluteHeight( slide ) ) / 2 ) - slidePadding, 0 ) + 'px'; + slide.style.top = Math.max( ( size.height - slide.scrollHeight ) / 2, 0 ) + 'px'; } } else { @@ -1646,7 +1659,7 @@ * Applies layout logic to the contents of all slides in * the presentation. */ - function layoutSlideContents( width, height, padding ) { + function layoutSlideContents( width, height ) { // Handle sizing of elements with the 'stretch' class toArray( dom.slides.querySelectorAll( 'section > .stretch' ) ).forEach( function( element ) { @@ -1779,6 +1792,17 @@ } } ); + // Calculate slide sizes + var margin = 70; + var slideSize = getComputedSlideSize(); + overviewSlideWidth = slideSize.width + margin; + overviewSlideHeight = slideSize.height + margin; + + // Reverse in RTL mode + if( config.rtl ) { + overviewSlideWidth = -overviewSlideWidth; + } + updateSlidesVisibility(); layoutOverview(); updateOverview(); @@ -1802,19 +1826,10 @@ */ function layoutOverview() { - var margin = 70; - var slideWidth = config.width + margin, - slideHeight = config.height + margin; - - // Reverse in RTL mode - if( config.rtl ) { - slideWidth = -slideWidth; - } - // Layout slides toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) { hslide.setAttribute( 'data-index-h', h ); - transformElement( hslide, 'translate3d(' + ( h * slideWidth ) + 'px, 0, 0)' ); + transformElement( hslide, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' ); if( hslide.classList.contains( 'stack' ) ) { @@ -1822,7 +1837,7 @@ vslide.setAttribute( 'data-index-h', h ); vslide.setAttribute( 'data-index-v', v ); - transformElement( vslide, 'translate3d(0, ' + ( v * slideHeight ) + 'px, 0)' ); + transformElement( vslide, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' ); } ); } @@ -1830,10 +1845,10 @@ // Layout slide backgrounds toArray( dom.background.childNodes ).forEach( function( hbackground, h ) { - transformElement( hbackground, 'translate3d(' + ( h * slideWidth ) + 'px, 0, 0)' ); + transformElement( hbackground, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' ); toArray( hbackground.querySelectorAll( '.slide-background' ) ).forEach( function( vbackground, v ) { - transformElement( vbackground, 'translate3d(0, ' + ( v * slideHeight ) + 'px, 0)' ); + transformElement( vbackground, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' ); } ); } ); @@ -1845,19 +1860,10 @@ */ function updateOverview() { - var margin = 70; - var slideWidth = config.width + margin, - slideHeight = config.height + margin; - - // Reverse in RTL mode - if( config.rtl ) { - slideWidth = -slideWidth; - } - transformSlides( { overview: [ - 'translateX('+ ( -indexh * slideWidth ) +'px)', - 'translateY('+ ( -indexv * slideHeight ) +'px)', + 'translateX('+ ( -indexh * overviewSlideWidth ) +'px)', + 'translateY('+ ( -indexv * overviewSlideHeight ) +'px)', 'translateZ('+ ( window.innerWidth < 400 ? -1000 : -2500 ) +'px)' ].join( ' ' ) } ); @@ -1974,10 +1980,10 @@ */ function enterFullscreen() { - var element = document.body; + var element = document.documentElement; // Check which implementation is available - var requestMethod = element.requestFullScreen || + var requestMethod = element.requestFullscreen || element.webkitRequestFullscreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || @@ -2318,6 +2324,23 @@ } /** + * Randomly shuffles all slides in the deck. + */ + function shuffle() { + + var slides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + slides.forEach( function( slide ) { + + // Insert this slide next to another random slide. This may + // cause the slide to insert before itself but that's fine. + dom.slides.insertBefore( slide, slides[ Math.floor( Math.random() * slides.length ) ] ); + + } ); + + } + + /** * Updates one dimension of slides by showing the slide * with the specified index. * @@ -2614,34 +2637,37 @@ .concat( dom.controlsNext ).forEach( function( node ) { node.classList.remove( 'enabled' ); node.classList.remove( 'fragmented' ); + + // Set 'disabled' attribute on all directions + node.setAttribute( 'disabled', 'disabled' ); } ); - // Add the 'enabled' class to the available routes - if( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); } ); - if( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); } ); - if( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); } ); - if( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); } ); + // Add the 'enabled' class to the available routes; remove 'disabled' attribute to enable buttons + if( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); // Prev/next buttons - if( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); } ); - if( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); } ); + if( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); // Highlight fragment directions if( currentSlide ) { // Always apply fragment decorator to prev/next buttons - if( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); - if( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); + if( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); // Apply fragment decorators to directional buttons based on // what slide axis they are in if( isVerticalSlide( currentSlide ) ) { - if( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); - if( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); + if( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); } else { - if( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); - if( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } ); + if( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); } } @@ -2722,8 +2748,20 @@ // Start video playback var currentVideo = currentBackground.querySelector( 'video' ); if( currentVideo ) { - if( currentVideo.currentTime > 0 ) currentVideo.currentTime = 0; - currentVideo.play(); + + var startVideo = function() { + currentVideo.currentTime = 0; + currentVideo.play(); + currentVideo.removeEventListener( 'loadeddata', startVideo ); + }; + + if( currentVideo.readyState > 1 ) { + startVideo(); + } + else { + currentVideo.addEventListener( 'loadeddata', startVideo ); + } + } var backgroundImageURL = currentBackground.style.backgroundImage || ''; @@ -2798,7 +2836,7 @@ horizontalOffsetMultiplier = config.parallaxBackgroundHorizontal; } else { - horizontalOffsetMultiplier = ( backgroundWidth - slideWidth ) / ( horizontalSlideCount-1 ); + horizontalOffsetMultiplier = horizontalSlideCount > 1 ? ( backgroundWidth - slideWidth ) / ( horizontalSlideCount-1 ) : 0; } horizontalOffset = horizontalOffsetMultiplier * indexh * -1; @@ -2870,6 +2908,7 @@ var backgroundImage = slide.getAttribute( 'data-background-image' ), backgroundVideo = slide.getAttribute( 'data-background-video' ), backgroundVideoLoop = slide.hasAttribute( 'data-background-video-loop' ), + backgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' ), backgroundIframe = slide.getAttribute( 'data-background-iframe' ); // Images @@ -2884,6 +2923,10 @@ video.setAttribute( 'loop', '' ); } + if( backgroundVideoMuted ) { + video.muted = true; + } + // Support comma separated lists of video sources backgroundVideo.split( ',' ).forEach( function( source ) { video.innerHTML += '<source src="'+ source +'">'; @@ -3372,10 +3415,7 @@ if( isPrintingPDF() ) { var slide = getSlide( x, y ); if( slide ) { - var background = slide.querySelector( '.slide-background' ); - if( background && background.parentNode === slide ) { - return background; - } + return slide.slideBackgroundElement; } return undefined; @@ -3688,7 +3728,10 @@ // - The overview isn't active // - The presentation isn't over if( autoSlide && !autoSlidePaused && !isPaused() && !isOverview() && ( !Reveal.isLastSlide() || availableFragments().next || config.loop === true ) ) { - autoSlideTimeout = setTimeout( navigateNext, autoSlide ); + autoSlideTimeout = setTimeout( function() { + typeof config.autoSlideMethod === 'function' ? config.autoSlideMethod() : navigateNext(); + cueAutoSlide(); + }, autoSlide ); autoSlideStartTime = Date.now(); } @@ -3834,10 +3877,6 @@ } } - // If auto-sliding is enabled we need to cue up - // another timeout - cueAutoSlide(); - } /** @@ -3909,10 +3948,11 @@ // the keyboard var activeElementIsCE = document.activeElement && document.activeElement.contentEditable !== 'inherit'; var activeElementIsInput = document.activeElement && document.activeElement.tagName && /input|textarea/i.test( document.activeElement.tagName ); + var activeElementIsNotes = document.activeElement && document.activeElement.className && /speaker-notes/i.test( document.activeElement.className); // Disregard the event if there's a focused element or a // keyboard modifier key is present - if( activeElementIsCE || activeElementIsInput || (event.shiftKey && event.keyCode !== 32) || event.altKey || event.ctrlKey || event.metaKey ) return; + if( activeElementIsCE || activeElementIsInput || activeElementIsNotes || (event.shiftKey && event.keyCode !== 32) || event.altKey || event.ctrlKey || event.metaKey ) return; // While paused only allow resume keyboard events; 'b', '.'' var resumeKeyCodes = [66,190,191]; @@ -4131,7 +4171,7 @@ } // There's a bug with swiping on some Android devices unless // the default action is always prevented - else if( navigator.userAgent.match( /android/gi ) ) { + else if( UA.match( /android/gi ) ) { event.preventDefault(); } @@ -4364,8 +4404,9 @@ function Playback( container, progressCheck ) { // Cosmetics - this.diameter = 50; - this.thickness = 3; + this.diameter = 100; + this.diameter2 = this.diameter/2; + this.thickness = 6; // Flags if we are currently playing this.playing = false; @@ -4383,6 +4424,8 @@ this.canvas.className = 'playback'; this.canvas.width = this.diameter; this.canvas.height = this.diameter; + this.canvas.style.width = this.diameter2 + 'px'; + this.canvas.style.height = this.diameter2 + 'px'; this.context = this.canvas.getContext( '2d' ); this.container.appendChild( this.canvas ); @@ -4433,10 +4476,10 @@ Playback.prototype.render = function() { var progress = this.playing ? this.progress : 0, - radius = ( this.diameter / 2 ) - this.thickness, - x = this.diameter / 2, - y = this.diameter / 2, - iconSize = 14; + radius = ( this.diameter2 ) - this.thickness, + x = this.diameter2, + y = this.diameter2, + iconSize = 28; // Ease towards 1 this.progressOffset += ( 1 - this.progressOffset ) * 0.1; @@ -4449,7 +4492,7 @@ // Solid background color this.context.beginPath(); - this.context.arc( x, y, radius + 2, 0, Math.PI * 2, false ); + this.context.arc( x, y, radius + 4, 0, Math.PI * 2, false ); this.context.fillStyle = 'rgba( 0, 0, 0, 0.4 )'; this.context.fill(); @@ -4474,14 +4517,14 @@ // Draw play/pause icons if( this.playing ) { this.context.fillStyle = '#fff'; - this.context.fillRect( 0, 0, iconSize / 2 - 2, iconSize ); - this.context.fillRect( iconSize / 2 + 2, 0, iconSize / 2 - 2, iconSize ); + this.context.fillRect( 0, 0, iconSize / 2 - 4, iconSize ); + this.context.fillRect( iconSize / 2 + 4, 0, iconSize / 2 - 4, iconSize ); } else { this.context.beginPath(); - this.context.translate( 2, 0 ); + this.context.translate( 4, 0 ); this.context.moveTo( 0, 0 ); - this.context.lineTo( iconSize - 2, iconSize / 2 ); + this.context.lineTo( iconSize - 4, iconSize / 2 ); this.context.lineTo( 0, iconSize ); this.context.fillStyle = '#fff'; this.context.fill(); @@ -4516,6 +4559,8 @@ Reveal = { + VERSION: VERSION, + initialize: initialize, configure: configure, sync: sync, @@ -4543,9 +4588,15 @@ navigatePrev: navigatePrev, navigateNext: navigateNext, + // Shows a help overlay with keyboard shortcuts + showHelp: showHelp, + // Forces an update in slide layout layout: layout, + // Randomizes the order of slides + shuffle: shuffle, + // Returns an object with the available routes as booleans (left/right/top/bottom) availableRoutes: availableRoutes, @@ -4669,6 +4720,11 @@ // Programatically triggers a keyboard event triggerKey: function( keyCode ) { onDocumentKeyDown( { keyCode: keyCode } ); + }, + + // Registers a new shortcut to include in the help overlay + registerKeyboardShortcut: function( key, value ) { + keyboardShortcuts[key] = value; } }; |