diff options
Diffstat (limited to 'js/reveal.js')
-rw-r--r-- | js/reveal.js | 376 |
1 files changed, 227 insertions, 149 deletions
diff --git a/js/reveal.js b/js/reveal.js index ef547aa..a48c92b 100644 --- a/js/reveal.js +++ b/js/reveal.js @@ -3,7 +3,7 @@ * http://lab.hakim.se/reveal-js * MIT licensed * - * Copyright (C) 2013 Hakim El Hattab, http://hakim.se + * Copyright (C) 2014 Hakim El Hattab, http://hakim.se */ var Reveal = (function(){ @@ -151,12 +151,6 @@ var Reveal = (function(){ // Delays updates to the URL due to a Chrome thumbnailer bug writeURLTimeout = 0, - // A delay used to activate the overview mode - activateOverviewTimeout = 0, - - // A delay used to deactivate the overview mode - deactivateOverviewTimeout = 0, - // Flags if the interaction event listeners are bound eventsAreBound = false, @@ -591,7 +585,13 @@ var Reveal = (function(){ enablePreviewLinks( '[data-preview-link]' ); } - // Auto-slide playback controls + // Remove existing auto-slide controls + if( autoSlidePlayer ) { + autoSlidePlayer.destroy(); + autoSlidePlayer = null; + } + + // Generate auto-slide controls if needed if( numberOfSlides > 1 && config.autoSlide && config.autoSlideStoppable && features.canvas && features.requestAnimationFrame ) { autoSlidePlayer = new Playback( dom.wrapper, function() { return Math.min( Math.max( ( Date.now() - autoSlideStartTime ) / autoSlide, 0 ), 1 ); @@ -600,9 +600,13 @@ var Reveal = (function(){ autoSlidePlayer.on( 'click', onAutoSlidePlayerClick ); autoSlidePaused = false; } - else if( autoSlidePlayer ) { - autoSlidePlayer.destroy(); - autoSlidePlayer = null; + + // When fragments are turned off they should be visible + if( config.fragments === false ) { + toArray( dom.slides.querySelectorAll( '.fragment' ) ).forEach( function( element ) { + element.classList.add( 'visible' ); + element.classList.remove( 'current-fragment' ); + } ); } // Load the theme in the config, if it's not already loaded @@ -753,6 +757,22 @@ var Reveal = (function(){ } /** + * Utility for deserializing a value. + */ + function deserialize( value ) { + + if( typeof value === 'string' ) { + if( value === 'null' ) return null; + else if( value === 'true' ) return true; + else if( value === 'false' ) return false; + else if( value.match( /^\d+$/ ) ) return parseFloat( value ); + } + + return value; + + } + + /** * Measures the distance in pixels between point a * and point b. * @@ -818,40 +838,26 @@ var Reveal = (function(){ /** * Returns the remaining height within the parent of the - * target element after subtracting the height of all - * siblings. + * target element. * - * remaining height = [parent height] - [ siblings height] + * remaining height = [ configured parent height ] - [ current parent height ] */ function getRemainingHeight( element, height ) { height = height || 0; if( element ) { - var parent = element.parentNode; - var siblings = parent.childNodes; - - // Subtract the height of each sibling - toArray( siblings ).forEach( function( sibling ) { + var newHeight, oldHeight = element.style.height; - if( typeof sibling.offsetHeight === 'number' && sibling !== element ) { + // Change the .stretch element height to 0 in order find the height of all + // the other elements + element.style.height = '0px'; + newHeight = height - element.parentNode.offsetHeight; - var styles = window.getComputedStyle( sibling ), - marginTop = parseInt( styles.marginTop, 10 ), - marginBottom = parseInt( styles.marginBottom, 10 ); - - height -= sibling.offsetHeight + marginTop + marginBottom; - - } - - } ); - - var elementStyles = window.getComputedStyle( element ); - - // Subtract the margins of the target element - height -= parseInt( elementStyles.marginTop, 10 ) + - parseInt( elementStyles.marginBottom, 10 ); + // Restore the old height, just in case + element.style.height = oldHeight + 'px'; + return newHeight; } return height; @@ -911,7 +917,7 @@ var Reveal = (function(){ function enableRollingLinks() { if( features.transforms3d && !( 'msPerspective' in document.body.style ) ) { - var anchors = document.querySelectorAll( SLIDES_SELECTOR + ' a:not(.image)' ); + var anchors = document.querySelectorAll( SLIDES_SELECTOR + ' a' ); for( var i = 0, len = anchors.length; i < len; i++ ) { var anchor = anchors[i]; @@ -1131,7 +1137,7 @@ var Reveal = (function(){ toArray( dom.slides.querySelectorAll( 'section > .stretch' ) ).forEach( function( element ) { // Determine how much vertical space we can use - var remainingHeight = getRemainingHeight( element, ( height - ( padding * 2 ) ) ); + var remainingHeight = getRemainingHeight( element, height ); // Consider the aspect ratio of media elements if( /(img|video)/gi.test( element.nodeName ) ) { @@ -1212,67 +1218,57 @@ var Reveal = (function(){ dom.wrapper.classList.add( 'overview' ); dom.wrapper.classList.remove( 'overview-deactivating' ); - clearTimeout( activateOverviewTimeout ); - clearTimeout( deactivateOverviewTimeout ); - - // Not the pretties solution, but need to let the overview - // class apply first so that slides are measured accurately - // before we can position them - activateOverviewTimeout = setTimeout( function() { - - var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ); - - for( var i = 0, len1 = horizontalSlides.length; i < len1; i++ ) { - var hslide = horizontalSlides[i], - hoffset = config.rtl ? -105 : 105; - - hslide.setAttribute( 'data-index-h', i ); + var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ); - // Apply CSS transform - transformElement( hslide, 'translateZ(-'+ depth +'px) translate(' + ( ( i - indexh ) * hoffset ) + '%, 0%)' ); + for( var i = 0, len1 = horizontalSlides.length; i < len1; i++ ) { + var hslide = horizontalSlides[i], + hoffset = config.rtl ? -105 : 105; - if( hslide.classList.contains( 'stack' ) ) { + hslide.setAttribute( 'data-index-h', i ); - var verticalSlides = hslide.querySelectorAll( 'section' ); + // Apply CSS transform + transformElement( hslide, 'translateZ(-'+ depth +'px) translate(' + ( ( i - indexh ) * hoffset ) + '%, 0%)' ); - for( var j = 0, len2 = verticalSlides.length; j < len2; j++ ) { - var verticalIndex = i === indexh ? indexv : getPreviousVerticalIndex( hslide ); + if( hslide.classList.contains( 'stack' ) ) { - var vslide = verticalSlides[j]; + var verticalSlides = hslide.querySelectorAll( 'section' ); - vslide.setAttribute( 'data-index-h', i ); - vslide.setAttribute( 'data-index-v', j ); + for( var j = 0, len2 = verticalSlides.length; j < len2; j++ ) { + var verticalIndex = i === indexh ? indexv : getPreviousVerticalIndex( hslide ); - // Apply CSS transform - transformElement( vslide, 'translate(0%, ' + ( ( j - verticalIndex ) * 105 ) + '%)' ); + var vslide = verticalSlides[j]; - // Navigate to this slide on click - vslide.addEventListener( 'click', onOverviewSlideClicked, true ); - } + vslide.setAttribute( 'data-index-h', i ); + vslide.setAttribute( 'data-index-v', j ); - } - else { + // Apply CSS transform + transformElement( vslide, 'translate(0%, ' + ( ( j - verticalIndex ) * 105 ) + '%)' ); // Navigate to this slide on click - hslide.addEventListener( 'click', onOverviewSlideClicked, true ); - + vslide.addEventListener( 'click', onOverviewSlideClicked, true ); } - } - updateSlidesVisibility(); + } + else { - layout(); + // Navigate to this slide on click + hslide.addEventListener( 'click', onOverviewSlideClicked, true ); - if( !wasActive ) { - // Notify observers of the overview showing - dispatchEvent( 'overviewshown', { - 'indexh': indexh, - 'indexv': indexv, - 'currentSlide': currentSlide - } ); } + } - }, 10 ); + updateSlidesVisibility(); + + layout(); + + if( !wasActive ) { + // Notify observers of the overview showing + dispatchEvent( 'overviewshown', { + 'indexh': indexh, + 'indexv': indexv, + 'currentSlide': currentSlide + } ); + } } @@ -1287,9 +1283,6 @@ var Reveal = (function(){ // Only proceed if enabled in config if( config.overview ) { - clearTimeout( activateOverviewTimeout ); - clearTimeout( deactivateOverviewTimeout ); - dom.wrapper.classList.remove( 'overview' ); // Temporarily add a class so that transitions can do different things @@ -1297,7 +1290,7 @@ var Reveal = (function(){ // moving from slide to slide dom.wrapper.classList.add( 'overview-deactivating' ); - deactivateOverviewTimeout = setTimeout( function () { + setTimeout( function () { dom.wrapper.classList.remove( 'overview-deactivating' ); }, 1 ); @@ -1384,7 +1377,7 @@ var Reveal = (function(){ element.webkitRequestFullscreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || - element.msRequestFullScreen; + element.msRequestFullscreen; if( requestMethod ) { requestMethod.apply( element ); @@ -1428,13 +1421,13 @@ var Reveal = (function(){ /** * Toggles the paused mode on and off. */ - function togglePause() { + function togglePause( override ) { - if( isPaused() ) { - resume(); + if( typeof override === 'boolean' ) { + override ? pause() : resume(); } else { - pause(); + isPaused() ? resume() : pause(); } } @@ -1750,26 +1743,30 @@ var Reveal = (function(){ // Any element previous to index is given the 'past' class element.classList.add( reverse ? 'future' : 'past' ); - var pastFragments = toArray( element.querySelectorAll( '.fragment' ) ); + if( config.fragments ) { + var pastFragments = toArray( element.querySelectorAll( '.fragment' ) ); - // Show all fragments on prior slides - while( pastFragments.length ) { - var pastFragment = pastFragments.pop(); - pastFragment.classList.add( 'visible' ); - pastFragment.classList.remove( 'current-fragment' ); + // Show all fragments on prior slides + while( pastFragments.length ) { + var pastFragment = pastFragments.pop(); + pastFragment.classList.add( 'visible' ); + pastFragment.classList.remove( 'current-fragment' ); + } } } else if( i > index ) { // Any element subsequent to index is given the 'future' class element.classList.add( reverse ? 'past' : 'future' ); - var futureFragments = toArray( element.querySelectorAll( '.fragment.visible' ) ); + if( config.fragments ) { + var futureFragments = toArray( element.querySelectorAll( '.fragment.visible' ) ); - // No fragments in future slides should be visible ahead of time - while( futureFragments.length ) { - var futureFragment = futureFragments.pop(); - futureFragment.classList.remove( 'visible' ); - futureFragment.classList.remove( 'current-fragment' ); + // No fragments in future slides should be visible ahead of time + while( futureFragments.length ) { + var futureFragment = futureFragments.pop(); + futureFragment.classList.remove( 'visible' ); + futureFragment.classList.remove( 'current-fragment' ); + } } } @@ -1864,42 +1861,7 @@ var Reveal = (function(){ // Update progress if enabled if( config.progress && dom.progress ) { - var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); - - // The number of past and total slides - var totalCount = document.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ).length; - var pastCount = 0; - - // Step through all slides and count the past ones - mainLoop: for( var i = 0; i < horizontalSlides.length; i++ ) { - - var horizontalSlide = horizontalSlides[i]; - var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ); - - for( var j = 0; j < verticalSlides.length; j++ ) { - - // Stop as soon as we arrive at the present - if( verticalSlides[j].classList.contains( 'present' ) ) { - break mainLoop; - } - - pastCount++; - - } - - // Stop as soon as we arrive at the present - if( horizontalSlide.classList.contains( 'present' ) ) { - break; - } - - // Don't count the wrapping section for vertical slides - if( horizontalSlide.classList.contains( 'stack' ) === false ) { - pastCount++; - } - - } - - dom.progressbar.style.width = ( pastCount / ( totalCount - 1 ) ) * window.innerWidth + 'px'; + dom.progressbar.style.width = getProgress() * window.innerWidth + 'px'; } @@ -2074,7 +2036,7 @@ var Reveal = (function(){ var slideHeight = dom.background.offsetHeight; var verticalSlideCount = verticalSlides.length; - var verticalOffset = verticalSlideCount > 0 ? -( backgroundHeight - slideHeight ) / ( verticalSlideCount-1 ) * indexv : 0; + var verticalOffset = verticalSlideCount > 1 ? -( backgroundHeight - slideHeight ) / ( verticalSlideCount-1 ) * indexv : 0; dom.background.style.backgroundPosition = horizontalOffset + 'px ' + verticalOffset + 'px'; @@ -2192,6 +2154,70 @@ var Reveal = (function(){ } /** + * Returns a value ranging from 0-1 that represents + * how far into the presentation we have navigated. + */ + function getProgress() { + + var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + // The number of past and total slides + var totalCount = document.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ).length; + var pastCount = 0; + + // Step through all slides and count the past ones + mainLoop: for( var i = 0; i < horizontalSlides.length; i++ ) { + + var horizontalSlide = horizontalSlides[i]; + var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ); + + for( var j = 0; j < verticalSlides.length; j++ ) { + + // Stop as soon as we arrive at the present + if( verticalSlides[j].classList.contains( 'present' ) ) { + break mainLoop; + } + + pastCount++; + + } + + // Stop as soon as we arrive at the present + if( horizontalSlide.classList.contains( 'present' ) ) { + break; + } + + // Don't count the wrapping section for vertical slides + if( horizontalSlide.classList.contains( 'stack' ) === false ) { + pastCount++; + } + + } + + if( currentSlide ) { + + var allFragments = currentSlide.querySelectorAll( '.fragment' ); + + // If there are fragments in the current slide those should be + // accounted for in the progress. + if( allFragments.length > 0 ) { + var visibleFragments = currentSlide.querySelectorAll( '.fragment.visible' ); + + // This value represents how big a portion of the slide progress + // that is made up by its fragments (0-1) + var fragmentWeight = 0.9; + + // Add fragment progress to the past slide count + pastCount += ( visibleFragments.length / allFragments.length ) * fragmentWeight; + } + + } + + return pastCount / ( totalCount - 1 ); + + } + + /** * Checks if this presentation is running inside of the * speaker notes window. */ @@ -2215,8 +2241,16 @@ var Reveal = (function(){ // If the first bit is invalid and there is a name we can // assume that this is a named link if( isNaN( parseInt( bits[0], 10 ) ) && name.length ) { - // Find the slide with the specified name - var element = document.querySelector( '#' + name ); + var element; + + try { + // Find the slide with the specified name + element = document.querySelector( '#' + name ); + } + catch( e ) { + // If the ID is an invalid selector a harmless SyntaxError + // may be thrown here. + } if( element ) { // Find the position of the named slide and navigate to it @@ -2261,9 +2295,16 @@ var Reveal = (function(){ else { var url = '/'; + // Attempt to create a named link based on the slide's ID + var id = currentSlide.getAttribute( 'id' ); + if( id ) { + id = id.toLowerCase(); + id = id.replace( /[^a-zA-Z0-9\-\_\:\.]/g, '' ); + } + // If the current slide has an ID, use that as a named link - if( currentSlide && typeof currentSlide.getAttribute( 'id' ) === 'string' ) { - url = '/' + currentSlide.getAttribute( 'id' ); + if( currentSlide && typeof id === 'string' && id.length ) { + url = '/' + id; } // Otherwise use the /h/v index else { @@ -2324,6 +2365,40 @@ var Reveal = (function(){ } /** + * Retrieves the current state of the presentation as + * an object. This state can then be restored at any + * time. + */ + function getState() { + + var indices = getIndices(); + + return { + indexh: indices.h, + indexv: indices.v, + indexf: indices.f, + paused: isPaused(), + overview: isOverview() + }; + + } + + /** + * Restores the presentation to the given state. + * + * @param {Object} state As generated by getState() + */ + function setState( state ) { + + if( typeof state === 'object' ) { + slide( deserialize( state.indexh ), deserialize( state.indexv ), deserialize( state.indexf ) ); + togglePause( deserialize( state.paused ) ); + toggleOverview( deserialize( state.overview ) ); + } + + } + + /** * Return a sorted fragments list, ordered by an increasing * "data-fragment-index" attribute. * @@ -2457,6 +2532,7 @@ var Reveal = (function(){ } updateControls(); + updateProgress(); return !!( fragmentsShown.length || fragmentsHidden.length ); @@ -3343,6 +3419,13 @@ var Reveal = (function(){ addEventListeners: addEventListeners, removeEventListeners: removeEventListeners, + // Facility for persisting and restoring the presentation state + getState: getState, + setState: setState, + + // Presentation progress on range of 0-1 + getProgress: getProgress, + // Returns the indices of the current, or specified, slide getIndices: getIndices, @@ -3390,12 +3473,7 @@ var Reveal = (function(){ for( var i in query ) { var value = query[ i ]; - query[ i ] = unescape( value ); - - if( value === 'null' ) query[ i ] = null; - else if( value === 'true' ) query[ i ] = true; - else if( value === 'false' ) query[ i ] = false; - else if( value.match( /^\d+$/ ) ) query[ i ] = parseFloat( value ); + query[ i ] = deserialize( unescape( value ) ); } return query; |