Newer
Older

Hakim El Hattab
committed
/**
* Retrieves the vertical index which was stored using

Hakim El Hattab
committed
* #setPreviousVerticalIndex() or 0 if no previous index
* exists.
*
* @param {HTMLElement} stack The vertical stack element
*/
function getPreviousVerticalIndex( stack ) {
if( typeof stack === 'object' && typeof stack.setAttribute === 'function' && stack.classList.contains( 'stack' ) ) {
// Prefer manually defined start-indexv
var attributeName = stack.hasAttribute( 'data-start-indexv' ) ? 'data-start-indexv' : 'data-previous-indexv';
return parseInt( stack.getAttribute( attributeName ) || 0, 10 );

Hakim El Hattab
committed
}
return 0;

Hakim El Hattab
committed
}
* Displays the overview of slides (quick nav) by
* scaling down and arranging all slide elements.
*
* Experimental feature, might be dropped if perf
* can't be improved.
*/
function activateOverview() {
// Only proceed if enabled in config
if( config.overview ) {
// Don't auto-slide while in overview mode
cancelAutoSlide();
var wasActive = dom.wrapper.classList.contains( 'overview' );
// Vary the depth of the overview based on screen size
var depth = window.innerWidth < 400 ? 1000 : 2500;
dom.wrapper.classList.add( 'overview' );
dom.wrapper.classList.remove( 'exit-overview' );
clearTimeout( activateOverviewTimeout );
// 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,
htransform = 'translateZ(-'+ depth +'px) translate(' + ( ( i - indexh ) * hoffset ) + '%, 0%)';
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
hslide.setAttribute( 'data-index-h', i );
hslide.style.WebkitTransform = htransform;
hslide.style.MozTransform = htransform;
hslide.style.msTransform = htransform;
hslide.style.OTransform = htransform;
hslide.style.transform = htransform;
if( hslide.classList.contains( 'stack' ) ) {
var verticalSlides = hslide.querySelectorAll( 'section' );
for( var j = 0, len2 = verticalSlides.length; j < len2; j++ ) {
var verticalIndex = i === indexh ? indexv : getPreviousVerticalIndex( hslide );
var vslide = verticalSlides[j],
vtransform = 'translate(0%, ' + ( ( j - verticalIndex ) * 105 ) + '%)';
vslide.setAttribute( 'data-index-h', i );
vslide.setAttribute( 'data-index-v', j );
vslide.style.WebkitTransform = vtransform;
vslide.style.MozTransform = vtransform;
vslide.style.msTransform = vtransform;
vslide.style.OTransform = vtransform;
vslide.style.transform = vtransform;
// Navigate to this slide on click
vslide.addEventListener( 'click', onOverviewSlideClicked, true );
}

Hakim El Hattab
committed
// Navigate to this slide on click
hslide.addEventListener( 'click', onOverviewSlideClicked, true );

Hakim El Hattab
committed
}

Hakim El Hattab
committed
updateSlidesVisibility();
if( !wasActive ) {
// Notify observers of the overview showing
dispatchEvent( 'overviewshown', {
'indexh': indexh,
'indexv': indexv,
'currentSlide': currentSlide
} );
}

Hakim El Hattab
committed
/**
* Exits the slide overview and enters the currently
* active slide.
*/
function deactivateOverview() {
// Only proceed if enabled in config
if( config.overview ) {
clearTimeout( activateOverviewTimeout );
dom.wrapper.classList.remove( 'overview' );
// Temporarily add a class so that transitions can do different things
// depending on whether they are exiting/entering overview, or just
// moving from slide to slide
dom.wrapper.classList.add( 'exit-overview' );
deactivateOverviewTimeout = setTimeout( function () {
dom.wrapper.classList.remove( 'exit-overview' );
}, 10);
// Select all slides
var slides = toArray( document.querySelectorAll( SLIDES_SELECTOR ) );
for( var i = 0, len = slides.length; i < len; i++ ) {
var element = slides[i];
element.style.display = '';
// Resets all transforms to use the external styles
element.style.WebkitTransform = '';
element.style.MozTransform = '';
element.style.msTransform = '';
element.style.OTransform = '';
element.style.transform = '';
element.removeEventListener( 'click', onOverviewSlideClicked, true );

Hakim El Hattab
committed
slide( indexh, indexv );
cueAutoSlide();
// Notify observers of the overview hiding
dispatchEvent( 'overviewhidden', {
'indexh': indexh,
'indexv': indexv,
'currentSlide': currentSlide
} );

Hakim El Hattab
committed
/**
* Toggles the slide overview mode on and off.
*
* @param {Boolean} override Optional flag which overrides the
* toggle logic and forcibly sets the desired state. True means

Hakim El Hattab
committed
* overview is open, false means it's closed.
*/
function toggleOverview( override ) {

Hakim El Hattab
committed
if( typeof override === 'boolean' ) {
override ? activateOverview() : deactivateOverview();
}
else {
isOverview() ? deactivateOverview() : activateOverview();

Hakim El Hattab
committed
}

Hakim El Hattab
committed
}
/**
* Checks if the overview is currently active.
* @return {Boolean} true if the overview is active,
* false otherwise
*/
function isOverview() {
return dom.wrapper.classList.contains( 'overview' );
/**
* Checks if the current or specified slide is vertical
* (nested within another slide).
*
* @param {HTMLElement} slide [optional] The slide to check
* orientation of
*/
function isVerticalSlide( slide ) {
// Prefer slide argument, otherwise use current slide
slide = slide ? slide : currentSlide;
return slide && !!slide.parentNode.nodeName.match( /section/i );
}
/**
* Handling the fullscreen functionality via the fullscreen API
*
* @see http://fullscreen.spec.whatwg.org/
* @see https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode
*/
function enterFullscreen() {
// Check which implementation is available
var requestMethod = element.requestFullScreen ||

Marc van Gend
committed
element.webkitRequestFullscreen ||
element.webkitRequestFullScreen ||
element.mozRequestFullScreen ||
element.msRequestFullScreen;
if( requestMethod ) {
requestMethod.apply( element );
}

Hakim El Hattab
committed
/**
* Enters the paused mode which fades everything on screen to

Hakim El Hattab
committed
* black.
*/
function pause() {
var wasPaused = dom.wrapper.classList.contains( 'paused' );
cancelAutoSlide();

Hakim El Hattab
committed
dom.wrapper.classList.add( 'paused' );
if( wasPaused === false ) {
dispatchEvent( 'paused' );
}

Hakim El Hattab
committed
}
/**
* Exits from the paused mode.
*/
function resume() {
var wasPaused = dom.wrapper.classList.contains( 'paused' );
dom.wrapper.classList.remove( 'paused' );
if( wasPaused ) {
dispatchEvent( 'resumed' );
}

Hakim El Hattab
committed
}
/**
* Toggles the paused mode on and off.
*/
function togglePause() {

Hakim El Hattab
committed
if( isPaused() ) {
resume();
}
else {
pause();
}

Hakim El Hattab
committed
}
/**
* Checks if we are currently in the paused mode.
*/
function isPaused() {

Hakim El Hattab
committed
return dom.wrapper.classList.contains( 'paused' );

Hakim El Hattab
committed
}
* Steps from the current point in the presentation to the
* slide which matches the specified horizontal and vertical
* indices.

Hakim El Hattab
committed
*
* @param {int} h Horizontal index of the target slide
* @param {int} v Vertical index of the target slide
* @param {int} f Optional index of a fragment within the
* target slide to activate
* @param {int} o Optional origin for use in multimaster environments

Hakim El Hattab
committed
// Remember where we were at before
previousSlide = currentSlide;
// Query all horizontal slides in the deck
var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR );
// If no vertical index is specified and the upcoming slide is a
// stack, resume at its previous vertical index

Hakim El Hattab
committed
if( v === undefined ) {
v = getPreviousVerticalIndex( horizontalSlides[ h ] );
// If we were on a vertical stack, remember what vertical index
// it was on so we can resume at the same position when returning
if( previousSlide && previousSlide.parentNode && previousSlide.parentNode.classList.contains( 'stack' ) ) {

Hakim El Hattab
committed
setPreviousVerticalIndex( previousSlide.parentNode, indexv );
// Remember the state before this slide
var stateBefore = state.concat();
// Reset the state array
state.length = 0;

Hakim El Hattab
committed
var indexhBefore = indexh,
indexvBefore = indexv;
// Activate and transition to the new slide
indexh = updateSlides( HORIZONTAL_SLIDES_SELECTOR, h === undefined ? indexh : h );
indexv = updateSlides( VERTICAL_SLIDES_SELECTOR, v === undefined ? indexv : v );

Hakim El Hattab
committed
// Update the visibility of slides now that the indices have changed
updateSlidesVisibility();

Hakim El Hattab
committed
// Apply the new state
stateLoop: for( var i = 0, len = state.length; i < len; i++ ) {
// Check if this state existed on the previous slide. If it
// did, we will avoid adding it repeatedly
for( var j = 0; j < stateBefore.length; j++ ) {
if( stateBefore[j] === state[i] ) {
stateBefore.splice( j, 1 );
continue stateLoop;
}
}

Hakim El Hattab
committed
document.documentElement.classList.add( state[i] );

Hakim El Hattab
committed
// Dispatch custom event matching the state's name
dispatchEvent( state[i] );
// Clean up the remains of the previous state
while( stateBefore.length ) {
document.documentElement.classList.remove( stateBefore.pop() );

Hakim El Hattab
committed
}
// If the overview is active, re-activate it to update positions
if( isOverview() ) {
activateOverview();
}

Hakim El Hattab
committed
// Find the current horizontal slide and any possible vertical slides
// within it
var currentHorizontalSlide = horizontalSlides[ indexh ],
currentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' );

Hakim El Hattab
committed

Hakim El Hattab
committed
// Store references to the previous and current slides
currentSlide = currentVerticalSlides[ indexv ] || currentHorizontalSlide;

Hakim El Hattab
committed
// Show fragment, if specified
if( typeof f !== 'undefined' ) {

Hakim El Hattab
committed
var fragments = sortFragments( currentSlide.querySelectorAll( '.fragment' ) );
toArray( fragments ).forEach( function( fragment, indexf ) {
if( indexf < f ) {
fragment.classList.add( 'visible' );
}
else {
fragment.classList.remove( 'visible' );
}
} );
}

Hakim El Hattab
committed
// Dispatch an event if the slide changed
var slideChanged = ( indexh !== indexhBefore || indexv !== indexvBefore );
if( slideChanged ) {
dispatchEvent( 'slidechanged', {

Hakim El Hattab
committed
'indexv': indexv,

Hakim El Hattab
committed
'previousSlide': previousSlide,
'currentSlide': currentSlide,
'origin': o
} );
}

Hakim El Hattab
committed
else {
// Ensure that the previous slide is never the same as the current
previousSlide = null;
}

Hakim El Hattab
committed
// Solves an edge case where the previous slide maintains the
// 'present' class when navigating between adjacent vertical

Hakim El Hattab
committed
// stacks
if( previousSlide ) {
previousSlide.classList.remove( 'present' );
// Reset all slides upon navigate to home
// Issue: #285
if ( document.querySelector( HOME_SLIDE_SELECTOR ).classList.contains( 'present' ) ) {
// Launch async task
setTimeout( function () {
var slides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.stack') ), i;
for( i in slides ) {
if( slides[i] ) {
// Reset stack
setPreviousVerticalIndex( slides[i], 0 );
}
}
}, 0 );
}

Hakim El Hattab
committed
}
// Handle embedded content
stopEmbeddedContent( previousSlide );
startEmbeddedContent( currentSlide );
}
updateControls();
updateProgress();
updateBackground();
// Update the URL hash
writeURL();
/**
* Syncs the presentation with the current DOM. Useful
* when new slides or control elements are added or when
* the configuration has changed.
*/
function sync() {
// Subscribe to input
removeEventListeners();
addEventListeners();
// Force a layout to make sure the current config is accounted for
layout();
// Reflect the current autoSlide value
autoSlide = config.autoSlide;
// Start auto-sliding if it's enabled
cueAutoSlide();
// Re-create the slide backgrounds
createBackgrounds();
updateControls();
updateProgress();
updateBackground();

Hakim El Hattab
committed
/**
* Updates one dimension of slides by showing the slide
* with the specified index.

Hakim El Hattab
committed
* @param {String} selector A CSS selector that will fetch
* the group of slides we are working with
* @param {Number} index The index of the slide that should be
* shown

Hakim El Hattab
committed
* @return {Number} The index of the slide that is now shown,
* might differ from the passed in index if it was out of

Hakim El Hattab
committed
* bounds.
*/
function updateSlides( selector, index ) {

Hakim El Hattab
committed
// Select all slides and convert the NodeList result to
// an array
var slides = toArray( document.querySelectorAll( selector ) ),

Hakim El Hattab
committed
slidesLength = slides.length;

Hakim El Hattab
committed
if( slidesLength ) {
// Should the index loop?
if( config.loop ) {
index %= slidesLength;
if( index < 0 ) {
index = slidesLength + index;
}
}

Hakim El Hattab
committed
// Enforce max and minimum index bounds
index = Math.max( Math.min( index, slidesLength - 1 ), 0 );

Hakim El Hattab
committed
for( var i = 0; i < slidesLength; i++ ) {
var element = slides[i];
var reverse = config.rtl && !isVerticalSlide( element );
element.classList.remove( 'past' );
element.classList.remove( 'present' );
element.classList.remove( 'future' );

Hakim El Hattab
committed
// http://www.w3.org/html/wg/drafts/html/master/editing.html#the-hidden-attribute
element.setAttribute( 'hidden', '' );

Hakim El Hattab
committed
if( i < index ) {
// Any element previous to index is given the 'past' class
element.classList.add( reverse ? 'future' : 'past' );

Hakim El Hattab
committed
}
else if( i > index ) {
// Any element subsequent to index is given the 'future' class
element.classList.add( reverse ? 'past' : 'future' );
var fragments = toArray( element.querySelectorAll( '.fragment.visible' ) );
// No fragments in future slides should be visible ahead of time
while( fragments.length ) {
fragments.pop().classList.remove( 'visible' );
}

Hakim El Hattab
committed
}
// If this element contains vertical slides
if( element.querySelector( 'section' ) ) {
element.classList.add( 'stack' );

Hakim El Hattab
committed
}
}
// Mark the current slide as present
slides[index].classList.add( 'present' );
slides[index].removeAttribute( 'hidden' );

Hakim El Hattab
committed
// If this slide has a state associated with it, add it
// onto the current state of the deck
var slideState = slides[index].getAttribute( 'data-state' );
if( slideState ) {
state = state.concat( slideState.split( ' ' ) );
}
// If this slide has a data-autoslide attribute associated use this as

Hakim El Hattab
committed
// autoSlide value otherwise use the global configured time
var slideAutoSlide = slides[index].getAttribute( 'data-autoslide' );
if( slideAutoSlide ) {
autoSlide = parseInt( slideAutoSlide, 10 );

Hakim El Hattab
committed
}

Hakim El Hattab
committed
}
else {
// Since there are no slides we can't be anywhere beyond the

Hakim El Hattab
committed
// zeroth index
index = 0;
}

Hakim El Hattab
committed
return index;

Hakim El Hattab
committed
}

Hakim El Hattab
committed
/**
* Optimization method; hide all slides that are far away
* from the present slide.
*/
function updateSlidesVisibility() {
// Select all slides and convert the NodeList result to
// an array
var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ),
horizontalSlidesLength = horizontalSlides.length,
distance;
if( horizontalSlidesLength ) {
// The number of steps away from the present slide that will
// be visible
var viewDistance = config.viewDistance;

Hakim El Hattab
committed
// Heavily limited on weaker devices
if( isMobileDevice ) {

Hakim El Hattab
committed
}
if( isOverview() ) {

Hakim El Hattab
committed
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
}
for( var x = 0; x < horizontalSlidesLength; x++ ) {
var horizontalSlide = horizontalSlides[x];
var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ),
verticalSlidesLength = verticalSlides.length;
if( verticalSlidesLength ) {
// Always show the vertical stack itself, even if its child
// slides are invisible
horizontalSlide.style.display = 'block';
for( var y = 0; y < verticalSlidesLength; y++ ) {
var verticalSlide = verticalSlides[y];
var dx = x - indexh,
dy = y - indexv;
distance = Math.sqrt( dx*dx + dy*dy );
verticalSlide.style.display = distance > viewDistance ? 'none' : 'block';

Hakim El Hattab
committed
}
}
else {
// Loops so that it measures 1 between the first and last slides
distance = Math.abs( ( indexh - x ) % ( horizontalSlidesLength - viewDistance ) ) || 0;

Hakim El Hattab
committed
horizontalSlide.style.display = distance > viewDistance ? 'none' : 'block';

Hakim El Hattab
committed
}
}
}
}
* Updates the progress bar to reflect the current slide.
*/
function updateProgress() {
// 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;
}
}
// 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';
}
}
/**
* Updates the state of all control/navigation arrows.
*/
function updateControls() {

Hakim El Hattab
committed
if ( config.controls && dom.controls ) {

Hakim El Hattab
committed
var routes = availableRoutes();
var fragments = availableFragments();

Hakim El Hattab
committed
// Remove the 'enabled' class from all directions

Hakim El Hattab
committed
dom.controlsLeft.concat( dom.controlsRight )
.concat( dom.controlsUp )
.concat( dom.controlsDown )
.concat( dom.controlsPrev )
.concat( dom.controlsNext ).forEach( function( node ) {

Hakim El Hattab
committed
node.classList.remove( 'enabled' );
node.classList.remove( 'fragmented' );

Hakim El Hattab
committed
} );
// 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' ); } );

Hakim El Hattab
committed
// 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' ); } );

Hakim El Hattab
committed
// 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' ); } );
// 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' ); } );
}
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' ); } );
}
}
* Updates the background elements to reflect the current
* slide.
*/
function updateBackground() {
// Update the classes of all backgrounds to match the
// states of their slides (past/present/future)
toArray( dom.background.childNodes ).forEach( function( backgroundh, h ) {
// Reverse past/future classes when in RTL mode
var horizontalPast = config.rtl ? 'future' : 'past',
horizontalFuture = config.rtl ? 'past' : 'future';
backgroundh.className = 'slide-background ' + ( h < indexh ? horizontalPast : h > indexh ? horizontalFuture : 'present' );
toArray( backgroundh.childNodes ).forEach( function( backgroundv, v ) {
backgroundv.className = 'slide-background ' + ( v < indexv ? 'past' : v > indexv ? 'future' : 'present' );
} );
} );
// Allow the first background to apply without transition
setTimeout( function() {
dom.background.classList.remove( 'no-transition' );
}, 1 );
* Determine what available routes there are for navigation.
* @return {Object} containing four booleans: left/right/up/down
*/
function availableRoutes() {
var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ),
verticalSlides = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR );
var routes = {
left: indexh > 0 || config.loop,
right: indexh < horizontalSlides.length - 1 || config.loop,
up: indexv > 0,
down: indexv < verticalSlides.length - 1
};
// reverse horizontal controls for rtl
if( config.rtl ) {
var left = routes.left;
routes.left = routes.right;
routes.right = left;
}
return routes;
/**
* Returns an object describing the available fragment
* directions.
*
* @return {Object} two boolean properties: prev/next
*/
function availableFragments() {
if( currentSlide && config.fragments ) {
var fragments = currentSlide.querySelectorAll( '.fragment' );
var hiddenFragments = currentSlide.querySelectorAll( '.fragment:not(.visible)' );
return {
prev: fragments.length - hiddenFragments.length > 0,
next: !!hiddenFragments.length
};
}
else {
return { prev: false, next: false };
}
}
/**
* Start playback of any embedded content inside of
* the targeted slide.
*/
function startEmbeddedContent( slide ) {
if( slide ) {
// HTML5 media elements
toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
if( el.hasAttribute( 'data-autoplay' ) ) {
el.play();
}
} );
// YouTube embeds
toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) {
if( el.hasAttribute( 'data-autoplay' ) ) {
el.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*');
}
});
}
}
/**
* Stop playback of any embedded content inside of
* the targeted slide.
*/
function stopEmbeddedContent( slide ) {
if( slide ) {
// HTML5 media elements
toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
if( !el.hasAttribute( 'data-ignore' ) ) {
el.pause();
}
} );
// YouTube embeds
toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) {
if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) {
el.contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}', '*');
}
});
}
}
/**
* Reads the current URL (hash) and navigates accordingly.
*/
function readURL() {
var hash = window.location.hash;
// Attempt to parse the hash as either an index or name
var bits = hash.slice( 2 ).split( '/' ),
name = hash.replace( /#|\//gi, '' );
// 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

Hakim El Hattab
committed
var element = document.querySelector( '#' + name );

Hakim El Hattab
committed
if( element ) {
// Find the position of the named slide and navigate to it

Hakim El Hattab
committed
var indices = Reveal.getIndices( element );
slide( indices.h, indices.v );
}
// If the slide doesn't exist, navigate to the current slide
else {

Hakim El Hattab
committed
slide( indexh, indexv );
}
}
else {
// Read the index components of the hash
var h = parseInt( bits[0], 10 ) || 0,
v = parseInt( bits[1], 10 ) || 0;

Tobi Reiss
committed
if( h !== indexh || v !== indexv ) {
slide( h, v );
}
/**
* Updates the page URL (hash) to reflect the current
* @param {Number} delay The time in ms to wait before
* writing the hash
function writeURL( delay ) {
if( config.history ) {
// Make sure there's never more than one timeout running
clearTimeout( writeURLTimeout );
// If a delay is specified, timeout this call
if( typeof delay === 'number' ) {
writeURLTimeout = setTimeout( writeURL, delay );

Hakim El Hattab
committed
}
else {
// If the current slide has an ID, use that as a named link
if( currentSlide && typeof currentSlide.getAttribute( 'id' ) === 'string' ) {
url = '/' + currentSlide.getAttribute( 'id' );
}
// Otherwise use the /h/v index
else {
if( indexh > 0 || indexv > 0 ) url += indexh;
if( indexv > 0 ) url += '/' + indexv;
}
window.location.hash = url;
}

Hakim El Hattab
committed
/**
* Retrieves the h/v location of the current, or specified,

Hakim El Hattab
committed
* slide.
*
* @param {HTMLElement} slide If specified, the returned
* index will be for this slide rather than the currently

Hakim El Hattab
committed
* active one

Hakim El Hattab
committed
* @return {Object} { h: <int>, v: <int>, f: <int> }

Hakim El Hattab
committed
*/
function getIndices( slide ) {

Hakim El Hattab
committed
// By default, return the current indices
var h = indexh,

Hakim El Hattab
committed
v = indexv,
f;

Hakim El Hattab
committed
// If a slide is specified, return the indices of that slide
if( slide ) {
var isVertical = isVerticalSlide( slide );

Hakim El Hattab
committed
var slideh = isVertical ? slide.parentNode : slide;
// Select all horizontal slides
var horizontalSlides = toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );

Hakim El Hattab
committed
// Now that we know which the horizontal slide is, get its index
h = Math.max( horizontalSlides.indexOf( slideh ), 0 );
// If this is a vertical slide, grab the vertical index
if( isVertical ) {
v = Math.max( toArray( slide.parentNode.querySelectorAll( 'section' ) ).indexOf( slide ), 0 );

Hakim El Hattab
committed
}
}

Hakim El Hattab
committed
if( !slide && currentSlide ) {
var hasFragments = currentSlide.querySelectorAll( '.fragment' ).length > 0;
if( hasFragments ) {
var visibleFragments = currentSlide.querySelectorAll( '.fragment.visible' );

Hakim El Hattab
committed
f = visibleFragments.length;
}
}
return { h: h, v: v, f: f };

Hakim El Hattab
committed
}