@ -68,6 +68,9 @@ var Reveal = (function(){
// by using a data-autoslide attribute on your slides
// by using a data-autoslide attribute on your slides
autoSlide : 0 ,
autoSlide : 0 ,
// Stop auto-sliding after user input
autoSlideStoppable : true ,
// Enable slide navigation via mouse wheel
// Enable slide navigation via mouse wheel
mouseWheel : false ,
mouseWheel : false ,
@ -80,6 +83,9 @@ var Reveal = (function(){
// Opens links in an iframe preview overlay
// Opens links in an iframe preview overlay
previewLinks : false ,
previewLinks : false ,
// Focuses body when page changes visiblity to ensure keyboard shortcuts work
focusBodyOnPageVisiblityChange : true ,
// Theme (see /css/theme)
// Theme (see /css/theme)
theme : null ,
theme : null ,
@ -92,6 +98,12 @@ var Reveal = (function(){
// Transition style for full page slide backgrounds
// Transition style for full page slide backgrounds
backgroundTransition : 'default' , // default/linear/none
backgroundTransition : 'default' , // default/linear/none
// Parallax background image
parallaxBackgroundImage : '' , // CSS syntax, e.g. "a.jpg"
// Parallax background size
parallaxBackgroundSize : '' , // CSS syntax, e.g. "3000px 2000px"
// Number of slides away from the current that are visible
// Number of slides away from the current that are visible
viewDistance : 3 ,
viewDistance : 3 ,
@ -102,9 +114,6 @@ var Reveal = (function(){
// Flags if reveal.js is loaded (has dispatched the 'ready' event)
// Flags if reveal.js is loaded (has dispatched the 'ready' event)
loaded = false ,
loaded = false ,
// The current auto-slide duration
autoSlide = 0 ,
// The horizontal and vertical index of the currently active slide
// The horizontal and vertical index of the currently active slide
indexh ,
indexh ,
indexv ,
indexv ,
@ -124,11 +133,8 @@ var Reveal = (function(){
// Cached references to DOM elements
// Cached references to DOM elements
dom = { } ,
dom = { } ,
// Client support for CSS 3D transforms, see #checkCapabilities()
// Features supported by the browser, see #checkCapabilities()
supports3DTransforms ,
features = { } ,
// Client support for CSS 2D transforms, see #checkCapabilities()
supports2DTransforms ,
// Client is a mobile device, see #checkCapabilities()
// Client is a mobile device, see #checkCapabilities()
isMobileDevice ,
isMobileDevice ,
@ -136,9 +142,6 @@ var Reveal = (function(){
// Throttles mouse wheel navigation
// Throttles mouse wheel navigation
lastMouseWheelStep = 0 ,
lastMouseWheelStep = 0 ,
// An interval used to automatically move on to the next slide
autoSlideTimeout = 0 ,
// Delays updates to the URL due to a Chrome thumbnailer bug
// Delays updates to the URL due to a Chrome thumbnailer bug
writeURLTimeout = 0 ,
writeURLTimeout = 0 ,
@ -151,6 +154,15 @@ var Reveal = (function(){
// Flags if the interaction event listeners are bound
// Flags if the interaction event listeners are bound
eventsAreBound = false ,
eventsAreBound = false ,
// The current auto-slide duration
autoSlide = 0 ,
// Auto slide properties
autoSlidePlayer ,
autoSlideTimeout = 0 ,
autoSlideStartTime = - 1 ,
autoSlidePaused = false ,
// Holds information about the currently ongoing touch input
// Holds information about the currently ongoing touch input
touch = {
touch = {
startX : 0 ,
startX : 0 ,
@ -168,7 +180,7 @@ var Reveal = (function(){
checkCapabilities ( ) ;
checkCapabilities ( ) ;
if ( ! supports2DTransforms && ! supports3DTransforms ) {
if ( ! features . transforms2d && ! features . transforms3d ) {
document . body . setAttribute ( 'class' , 'no-transforms' ) ;
document . body . setAttribute ( 'class' , 'no-transforms' ) ;
// If the browser doesn't support core features we won't be
// If the browser doesn't support core features we won't be
@ -181,6 +193,7 @@ var Reveal = (function(){
// Copy options over to our config object
// Copy options over to our config object
extend ( config , options ) ;
extend ( config , options ) ;
extend ( config , Reveal . getQueryHash ( ) ) ;
// Hide the address bar in mobile browsers
// Hide the address bar in mobile browsers
hideAddressBar ( ) ;
hideAddressBar ( ) ;
@ -196,18 +209,23 @@ var Reveal = (function(){
* /
* /
function checkCapabilities ( ) {
function checkCapabilities ( ) {
supports3DTransforms = 'WebkitPerspective' in document . body . style ||
features . transforms3d = 'WebkitPerspective' in document . body . style ||
'MozPerspective' in document . body . style ||
'MozPerspective' in document . body . style ||
'msPerspective' in document . body . style ||
'msPerspective' in document . body . style ||
'OPerspective' in document . body . style ||
'OPerspective' in document . body . style ||
'perspective' in document . body . style ;
'perspective' in document . body . style ;
supports2DTransforms = 'WebkitTransform' in document . body . style ||
features . transforms2d = 'WebkitTransform' in document . body . style ||
'MozTransform' in document . body . style ||
'MozTransform' in document . body . style ||
'msTransform' in document . body . style ||
'msTransform' in document . body . style ||
'OTransform' in document . body . style ||
'OTransform' in document . body . style ||
'transform' in document . body . style ;
'transform' in document . body . style ;
features . requestAnimationFrameMethod = window . requestAnimationFrame || window . webkitRequestAnimationFrame || window . mozRequestAnimationFrame ;
features . requestAnimationFrame = typeof features . requestAnimationFrameMethod === 'function' ;
features . canvas = ! ! document . createElement ( 'canvas' ) . getContext ;
isMobileDevice = navigator . userAgent . match ( /(iphone|ipod|android)/gi ) ;
isMobileDevice = navigator . userAgent . match ( /(iphone|ipod|android)/gi ) ;
}
}
@ -422,7 +440,7 @@ var Reveal = (function(){
if ( data . background ) {
if ( data . background ) {
// Auto-wrap image urls in url(...)
// Auto-wrap image urls in url(...)
if ( /^(http|file|\/\/)/gi . test ( data . background ) || /\.(png|jpg|jpeg|gif|bmp)$/gi . test ( data . background ) ) {
if ( /^(http|file|\/\/)/gi . test ( data . background ) || /\.(svg| png|jpg|jpeg|gif|bmp)$/gi . test ( data . background ) ) {
element . style . backgroundImage = 'url(' + data . background + ')' ;
element . style . backgroundImage = 'url(' + data . background + ')' ;
}
}
else {
else {
@ -470,6 +488,28 @@ var Reveal = (function(){
} ) ;
} ) ;
// Add parallax background if specified
if ( config . parallaxBackgroundImage ) {
dom . background . style . backgroundImage = 'url("' + config . parallaxBackgroundImage + '")' ;
dom . background . style . backgroundSize = config . parallaxBackgroundSize ;
// Make sure the below properties are set on the element - these properties are
// needed for proper transitions to be set on the element via CSS. To remove
// annoying background slide-in effect when the presentation starts, apply
// these properties after short time delay
setTimeout ( function ( ) {
dom . wrapper . classList . add ( 'has-parallax-background' ) ;
} , 1 ) ;
}
else {
dom . background . style . backgroundImage = '' ;
dom . wrapper . classList . remove ( 'has-parallax-background' ) ;
}
}
}
/ * *
/ * *
@ -478,6 +518,8 @@ var Reveal = (function(){
* /
* /
function configure ( options ) {
function configure ( options ) {
var numberOfSlides = document . querySelectorAll ( SLIDES_SELECTOR ) . length ;
dom . wrapper . classList . remove ( config . transition ) ;
dom . wrapper . classList . remove ( config . transition ) ;
// New config options may be passed when this method
// New config options may be passed when this method
@ -485,7 +527,7 @@ var Reveal = (function(){
if ( typeof options === 'object' ) extend ( config , options ) ;
if ( typeof options === 'object' ) extend ( config , options ) ;
// Force linear transition based on browser capabilities
// Force linear transition based on browser capabilities
if ( supports3DTransforms === false ) config . transition = 'linear' ;
if ( features . transforms3d === false ) config . transition = 'linear' ;
dom . wrapper . classList . add ( config . transition ) ;
dom . wrapper . classList . add ( config . transition ) ;
@ -535,6 +577,20 @@ var Reveal = (function(){
enablePreviewLinks ( '[data-preview-link]' ) ;
enablePreviewLinks ( '[data-preview-link]' ) ;
}
}
// Auto-slide playback controls
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 ) ;
} ) ;
autoSlidePlayer . on ( 'click' , onAutoSlidePlayerClick ) ;
autoSlidePaused = false ;
}
else if ( autoSlidePlayer ) {
autoSlidePlayer . destroy ( ) ;
autoSlidePlayer = null ;
}
// Load the theme in the config, if it's not already loaded
// Load the theme in the config, if it's not already loaded
if ( config . theme && dom . theme ) {
if ( config . theme && dom . theme ) {
var themeURL = dom . theme . getAttribute ( 'href' ) ;
var themeURL = dom . theme . getAttribute ( 'href' ) ;
@ -578,10 +634,28 @@ var Reveal = (function(){
document . addEventListener ( 'keydown' , onDocumentKeyDown , false ) ;
document . addEventListener ( 'keydown' , onDocumentKeyDown , false ) ;
}
}
if ( config . progress && dom . progress ) {
if ( config . progress && dom . progress ) {
dom . progress . addEventListener ( 'click' , onProgressClicked , false ) ;
dom . progress . addEventListener ( 'click' , onProgressClicked , false ) ;
}
}
if ( config . focusBodyOnPageVisiblityChange ) {
var visibilityChange ;
if ( 'hidden' in document ) {
visibilityChange = 'visibilitychange' ;
}
else if ( 'msHidden' in document ) {
visibilityChange = 'msvisibilitychange' ;
}
else if ( 'webkitHidden' in document ) {
visibilityChange = 'webkitvisibilitychange' ;
}
if ( visibilityChange ) {
document . addEventListener ( visibilityChange , onPageVisibilityChange , false ) ;
}
}
[ 'touchstart' , 'click' ] . forEach ( function ( eventName ) {
[ 'touchstart' , 'click' ] . forEach ( function ( eventName ) {
dom . controlsLeft . forEach ( function ( el ) { el . addEventListener ( eventName , onNavigateLeftClicked , false ) ; } ) ;
dom . controlsLeft . forEach ( function ( el ) { el . addEventListener ( eventName , onNavigateLeftClicked , false ) ; } ) ;
dom . controlsRight . forEach ( function ( el ) { el . addEventListener ( eventName , onNavigateRightClicked , false ) ; } ) ;
dom . controlsRight . forEach ( function ( el ) { el . addEventListener ( eventName , onNavigateRightClicked , false ) ; } ) ;
@ -784,16 +858,6 @@ var Reveal = (function(){
* /
* /
function removeAddressBar ( ) {
function removeAddressBar ( ) {
// Portrait and not Chrome for iOS
if ( window . orientation === 0 && ! /crios/gi . test ( navigator . userAgent ) ) {
document . documentElement . style . overflow = 'scroll' ;
document . body . style . height = '120%' ;
}
else {
document . documentElement . style . overflow = '' ;
document . body . style . height = '100%' ;
}
setTimeout ( function ( ) {
setTimeout ( function ( ) {
window . scrollTo ( 0 , 1 ) ;
window . scrollTo ( 0 , 1 ) ;
} , 10 ) ;
} , 10 ) ;
@ -818,7 +882,7 @@ var Reveal = (function(){
* /
* /
function enableRollingLinks ( ) {
function enableRollingLinks ( ) {
if ( supports3DTransforms && ! ( 'msPerspective' in document . body . style ) ) {
if ( features . transforms3d && ! ( 'msPerspective' in document . body . style ) ) {
var anchors = document . querySelectorAll ( SLIDES_SELECTOR + ' a:not(.image)' ) ;
var anchors = document . querySelectorAll ( SLIDES_SELECTOR + ' a:not(.image)' ) ;
for ( var i = 0 , len = anchors . length ; i < len ; i ++ ) {
for ( var i = 0 , len = anchors . length ; i < len ; i ++ ) {
@ -1055,6 +1119,7 @@ var Reveal = (function(){
}
}
updateProgress ( ) ;
updateProgress ( ) ;
updateParallax ( ) ;
}
}
@ -1471,7 +1536,6 @@ var Reveal = (function(){
// Store references to the previous and current slides
// Store references to the previous and current slides
currentSlide = currentVerticalSlides [ indexv ] || currentHorizontalSlide ;
currentSlide = currentVerticalSlides [ indexv ] || currentHorizontalSlide ;
// Show fragment, if specified
// Show fragment, if specified
if ( typeof f !== 'undefined' ) {
if ( typeof f !== 'undefined' ) {
var fragments = sortFragments ( currentSlide . querySelectorAll ( '.fragment' ) ) ;
var fragments = sortFragments ( currentSlide . querySelectorAll ( '.fragment' ) ) ;
@ -1533,10 +1597,13 @@ var Reveal = (function(){
updateControls ( ) ;
updateControls ( ) ;
updateProgress ( ) ;
updateProgress ( ) ;
updateBackground ( ) ;
updateBackground ( ) ;
updateParallax ( ) ;
// Update the URL hash
// Update the URL hash
writeURL ( ) ;
writeURL ( ) ;
cueAutoSlide ( ) ;
}
}
/ * *
/ * *
@ -1647,18 +1714,6 @@ var Reveal = (function(){
state = state . concat ( slideState . split ( ' ' ) ) ;
state = state . concat ( slideState . split ( ' ' ) ) ;
}
}
// If this slide has a data-autoslide attribute associated use this as
// autoSlide value otherwise use the global configured time
var slideAutoSlide = slides [ index ] . getAttribute ( 'data-autoslide' ) ;
if ( slideAutoSlide ) {
autoSlide = parseInt ( slideAutoSlide , 10 ) ;
}
else {
autoSlide = config . autoSlide ;
}
cueAutoSlide ( ) ;
}
}
else {
else {
// Since there are no slides we can't be anywhere beyond the
// Since there are no slides we can't be anywhere beyond the
@ -1855,6 +1910,42 @@ var Reveal = (function(){
}
}
/ * *
* Updates the position of the parallax background based
* on the current slide index .
* /
function updateParallax ( ) {
if ( config . parallaxBackgroundImage ) {
var horizontalSlides = document . querySelectorAll ( HORIZONTAL_SLIDES_SELECTOR ) ,
verticalSlides = document . querySelectorAll ( VERTICAL_SLIDES_SELECTOR ) ;
var backgroundSize = dom . background . style . backgroundSize . split ( ' ' ) ,
backgroundWidth , backgroundHeight ;
if ( backgroundSize . length === 1 ) {
backgroundWidth = backgroundHeight = parseInt ( backgroundSize [ 0 ] , 10 ) ;
}
else {
backgroundWidth = parseInt ( backgroundSize [ 0 ] , 10 ) ;
backgroundHeight = parseInt ( backgroundSize [ 1 ] , 10 ) ;
}
var slideWidth = dom . background . offsetWidth ;
var horizontalSlideCount = horizontalSlides . length ;
var horizontalOffset = - ( backgroundWidth - slideWidth ) / ( horizontalSlideCount - 1 ) * indexh ;
var slideHeight = dom . background . offsetHeight ;
var verticalSlideCount = verticalSlides . length ;
var verticalOffset = verticalSlideCount > 0 ? - ( backgroundHeight - slideHeight ) / ( verticalSlideCount - 1 ) * indexv : 0 ;
dom . background . style . backgroundPosition = horizontalOffset + 'px ' + verticalOffset + 'px' ;
}
}
/ * *
/ * *
* Determine what available routes there are for navigation .
* Determine what available routes there are for navigation .
*
*
@ -2149,11 +2240,35 @@ var Reveal = (function(){
* /
* /
function cueAutoSlide ( ) {
function cueAutoSlide ( ) {
clearTimeout ( autoSlideTimeout ) ;
cancelAutoSlide ( ) ;
// Cue the next auto-slide if enabled
if ( currentSlide ) {
if ( autoSlide && ! isPaused ( ) && ! isOverview ( ) ) {
// If the current slide has a data-autoslide use that,
// otherwise use the config.autoSlide value
var slideAutoSlide = currentSlide . getAttribute ( 'data-autoslide' ) ;
if ( slideAutoSlide ) {
autoSlide = parseInt ( slideAutoSlide , 10 ) ;
}
else {
autoSlide = config . autoSlide ;
}
// Cue the next auto-slide if:
// - There is an autoSlide value
// - Auto-sliding isn't paused by the user
// - The presentation isn't paused
// - The overview isn't active
// - The presentation isn't over
if ( autoSlide && ! autoSlidePaused && ! isPaused ( ) && ! isOverview ( ) && ( ! Reveal . isLastSlide ( ) || config . loop === true ) ) {
autoSlideTimeout = setTimeout ( navigateNext , autoSlide ) ;
autoSlideTimeout = setTimeout ( navigateNext , autoSlide ) ;
autoSlideStartTime = Date . now ( ) ;
}
if ( autoSlidePlayer ) {
autoSlidePlayer . setPlaying ( autoSlideTimeout !== - 1 ) ;
}
}
}
}
}
@ -2164,6 +2279,25 @@ var Reveal = (function(){
function cancelAutoSlide ( ) {
function cancelAutoSlide ( ) {
clearTimeout ( autoSlideTimeout ) ;
clearTimeout ( autoSlideTimeout ) ;
autoSlideTimeout = - 1 ;
}
function pauseAutoSlide ( ) {
autoSlidePaused = true ;
clearTimeout ( autoSlideTimeout ) ;
if ( autoSlidePlayer ) {
autoSlidePlayer . setPlaying ( false ) ;
}
}
function resumeAutoSlide ( ) {
autoSlidePaused = false ;
cueAutoSlide ( ) ;
}
}
@ -2263,14 +2397,25 @@ var Reveal = (function(){
// ----------------------------- EVENTS -------------------------------//
// ----------------------------- EVENTS -------------------------------//
// --------------------------------------------------------------------//
// --------------------------------------------------------------------//
/ * *
* Called by all event handlers that are based on user
* input .
* /
function onUserInput ( event ) {
if ( config . autoSlideStoppable ) {
pauseAutoSlide ( ) ;
}
}
/ * *
/ * *
* Handler for the document level 'keydown' event .
* Handler for the document level 'keydown' event .
*
* @ param { Object } event
* /
* /
function onDocumentKeyDown ( event ) {
function onDocumentKeyDown ( event ) {
onUserInput ( event ) ;
// Check if there's a focused element that could be using
// Check if there's a focused element that could be using
// the keyboard
// the keyboard
var activeElement = document . activeElement ;
var activeElement = document . activeElement ;
@ -2357,8 +2502,13 @@ var Reveal = (function(){
event . preventDefault ( ) ;
event . preventDefault ( ) ;
}
}
// ESC or O key
// ESC or O key
else if ( ( event . keyCode === 27 || event . keyCode === 79 ) && supports3DTransforms ) {
else if ( ( event . keyCode === 27 || event . keyCode === 79 ) && features . transforms3d ) {
if ( dom . preview ) {
closePreview ( ) ;
}
else {
toggleOverview ( ) ;
toggleOverview ( ) ;
}
event . preventDefault ( ) ;
event . preventDefault ( ) ;
}
}
@ -2400,6 +2550,8 @@ var Reveal = (function(){
// Each touch should only trigger one action
// Each touch should only trigger one action
if ( ! touch . captured ) {
if ( ! touch . captured ) {
onUserInput ( event ) ;
var currentX = event . touches [ 0 ] . clientX ;
var currentX = event . touches [ 0 ] . clientX ;
var currentY = event . touches [ 0 ] . clientY ;
var currentY = event . touches [ 0 ] . clientY ;
@ -2553,6 +2705,8 @@ var Reveal = (function(){
* /
* /
function onProgressClicked ( event ) {
function onProgressClicked ( event ) {
onUserInput ( event ) ;
event . preventDefault ( ) ;
event . preventDefault ( ) ;
var slidesTotal = toArray ( document . querySelectorAll ( HORIZONTAL_SLIDES_SELECTOR ) ) . length ;
var slidesTotal = toArray ( document . querySelectorAll ( HORIZONTAL_SLIDES_SELECTOR ) ) . length ;
@ -2565,12 +2719,12 @@ var Reveal = (function(){
/ * *
/ * *
* Event handler for navigation control buttons .
* Event handler for navigation control buttons .
* /
* /
function onNavigateLeftClicked ( event ) { event . preventDefault ( ) ; navigateLeft ( ) ; }
function onNavigateLeftClicked ( event ) { event . preventDefault ( ) ; onUserInput ( ) ; navigateLeft ( ) ; }
function onNavigateRightClicked ( event ) { event . preventDefault ( ) ; navigateRight ( ) ; }
function onNavigateRightClicked ( event ) { event . preventDefault ( ) ; onUserInput ( ) ; navigateRight ( ) ; }
function onNavigateUpClicked ( event ) { event . preventDefault ( ) ; navigateUp ( ) ; }
function onNavigateUpClicked ( event ) { event . preventDefault ( ) ; onUserInput ( ) ; navigateUp ( ) ; }
function onNavigateDownClicked ( event ) { event . preventDefault ( ) ; navigateDown ( ) ; }
function onNavigateDownClicked ( event ) { event . preventDefault ( ) ; onUserInput ( ) ; navigateDown ( ) ; }
function onNavigatePrevClicked ( event ) { event . preventDefault ( ) ; navigatePrev ( ) ; }
function onNavigatePrevClicked ( event ) { event . preventDefault ( ) ; onUserInput ( ) ; navigatePrev ( ) ; }
function onNavigateNextClicked ( event ) { event . preventDefault ( ) ; navigateNext ( ) ; }
function onNavigateNextClicked ( event ) { event . preventDefault ( ) ; onUserInput ( ) ; navigateNext ( ) ; }
/ * *
/ * *
* Handler for the window level 'hashchange' event .
* Handler for the window level 'hashchange' event .
@ -2590,6 +2744,24 @@ var Reveal = (function(){
}
}
/ * *
* Handle for the window level 'visibilitychange' event .
* /
function onPageVisibilityChange ( event ) {
var isHidden = document . webkitHidden ||
document . msHidden ||
document . hidden ;
// If, after clicking a link or similar and we're coming back,
// focus the document.body to ensure we can use keyboard shortcuts
if ( isHidden === false && document . activeElement !== document . body ) {
document . activeElement . blur ( ) ;
document . body . focus ( ) ;
}
}
/ * *
/ * *
* Invoked when a slide is and we ' re in the overview .
* Invoked when a slide is and we ' re in the overview .
* /
* /
@ -2636,6 +2808,191 @@ var Reveal = (function(){
}
}
/ * *
* Handles click on the auto - sliding controls element .
* /
function onAutoSlidePlayerClick ( event ) {
// Replay
if ( Reveal . isLastSlide ( ) && config . loop === false ) {
slide ( 0 , 0 ) ;
resumeAutoSlide ( ) ;
}
// Resume
else if ( autoSlidePaused ) {
resumeAutoSlide ( ) ;
}
// Pause
else {
pauseAutoSlide ( ) ;
}
}
// --------------------------------------------------------------------//
// ------------------------ PLAYBACK COMPONENT ------------------------//
// --------------------------------------------------------------------//
/ * *
* Constructor for the playback component , which displays
* play / pause / progress controls .
*
* @ param { HTMLElement } container The component will append
* itself to this
* @ param { Function } progressCheck A method which will be
* called frequently to get the current progress on a range
* of 0 - 1
* /
function Playback ( container , progressCheck ) {
// Cosmetics
this . diameter = 50 ;
this . thickness = 3 ;
// Flags if we are currently playing
this . playing = false ;
// Current progress on a 0-1 range
this . progress = 0 ;
// Used to loop the animation smoothly
this . progressOffset = 1 ;
this . container = container ;
this . progressCheck = progressCheck ;
this . canvas = document . createElement ( 'canvas' ) ;
this . canvas . className = 'playback' ;
this . canvas . width = this . diameter ;
this . canvas . height = this . diameter ;
this . context = this . canvas . getContext ( '2d' ) ;
this . container . appendChild ( this . canvas ) ;
this . render ( ) ;
}
Playback . prototype . setPlaying = function ( value ) {
var wasPlaying = this . playing ;
this . playing = value ;
// Start repainting if we weren't already
if ( ! wasPlaying && this . playing ) {
this . animate ( ) ;
}
else {
this . render ( ) ;
}
} ;
Playback . prototype . animate = function ( ) {
var progressBefore = this . progress ;
this . progress = this . progressCheck ( ) ;
// When we loop, offset the progress so that it eases
// smoothly rather than immediately resetting
if ( progressBefore > 0.8 && this . progress < 0.2 ) {
this . progressOffset = this . progress ;
}
this . render ( ) ;
if ( this . playing ) {
features . requestAnimationFrameMethod . call ( window , this . animate . bind ( this ) ) ;
}
} ;
/ * *
* Renders the current progress and playback state .
* /
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 ;
// Ease towards 1
this . progressOffset += ( 1 - this . progressOffset ) * 0.1 ;
var endAngle = ( - Math . PI / 2 ) + ( progress * ( Math . PI * 2 ) ) ;
var startAngle = ( - Math . PI / 2 ) + ( this . progressOffset * ( Math . PI * 2 ) ) ;
this . context . save ( ) ;
this . context . clearRect ( 0 , 0 , this . diameter , this . diameter ) ;
// Solid background color
this . context . beginPath ( ) ;
this . context . arc ( x , y , radius + 2 , 0 , Math . PI * 2 , false ) ;
this . context . fillStyle = 'rgba( 0, 0, 0, 0.4 )' ;
this . context . fill ( ) ;
// Draw progress track
this . context . beginPath ( ) ;
this . context . arc ( x , y , radius , 0 , Math . PI * 2 , false ) ;
this . context . lineWidth = this . thickness ;
this . context . strokeStyle = '#666' ;
this . context . stroke ( ) ;
if ( this . playing ) {
// Draw progress on top of track
this . context . beginPath ( ) ;
this . context . arc ( x , y , radius , startAngle , endAngle , false ) ;
this . context . lineWidth = this . thickness ;
this . context . strokeStyle = '#fff' ;
this . context . stroke ( ) ;
}
this . context . translate ( x - ( iconSize / 2 ) , y - ( iconSize / 2 ) ) ;
// 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 ) ;
}
else {
this . context . beginPath ( ) ;
this . context . translate ( 2 , 0 ) ;
this . context . moveTo ( 0 , 0 ) ;
this . context . lineTo ( iconSize - 2 , iconSize / 2 ) ;
this . context . lineTo ( 0 , iconSize ) ;
this . context . fillStyle = '#fff' ;
this . context . fill ( ) ;
}
this . context . restore ( ) ;
} ;
Playback . prototype . on = function ( type , listener ) {
this . canvas . addEventListener ( type , listener , false ) ;
} ;
Playback . prototype . off = function ( type , listener ) {
this . canvas . removeEventListener ( type , listener , false ) ;
} ;
Playback . prototype . destroy = function ( ) {
this . playing = false ;
if ( this . canvas . parentNode ) {
this . container . removeChild ( this . canvas ) ;
}
} ;
// --------------------------------------------------------------------//
// --------------------------------------------------------------------//
// ------------------------------- API --------------------------------//
// ------------------------------- API --------------------------------//
@ -2733,6 +3090,15 @@ var Reveal = (function(){
query [ a . split ( '=' ) . shift ( ) ] = a . split ( '=' ) . pop ( ) ;
query [ a . split ( '=' ) . shift ( ) ] = a . split ( '=' ) . pop ( ) ;
} ) ;
} ) ;
// Basic deserialization
for ( var i in query ) {
var value = query [ i ] ;
if ( value === 'null' ) query [ i ] = null ;
else if ( value === 'true' ) query [ i ] = true ;
else if ( value === 'false' ) query [ i ] = false ;
else if ( ! isNaN ( parseFloat ( value ) ) ) query [ i ] = parseFloat ( value ) ;
}
return query ;
return query ;
} ,
} ,