Newer
Older
* reveal.js 2.1 r35
* http://lab.hakim.se/reveal-js
* MIT licensed
* Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
var Reveal = (function(){
'use strict';
var HORIZONTAL_SLIDES_SELECTOR = '.reveal .slides>section',
VERTICAL_SLIDES_SELECTOR = '.reveal .slides>section.present>section',

Hakim El Hattab
committed
// Configurations defaults, can be overridden at initialization time

Hakim El Hattab
committed
// Display controls in the bottom right corner

Hakim El Hattab
committed
// Display a presentation progress bar
progress: true,
// Push each slide change to the browser history

Hakim El Hattab
committed
// Enable keyboard shortcuts for navigation
keyboard: true,
// Enable the slide overview mode
overview: true,

Hakim El Hattab
committed
// Loop the presentation

Hakim El Hattab
committed
// Number of milliseconds between automatically proceeding to the
// next slide, disabled when set to 0, this value can be overwritten
// by using a data-autoslide attribute on your slides

Hakim El Hattab
committed
autoSlide: 0,
// Enable slide navigation via mouse wheel

Hakim El Hattab
committed
// Apply a 3D roll to links on hover

Hakim El Hattab
committed
// Transition style (see /css/theme)

Hakim El Hattab
committed
// Transition style
transition: 'default', // default/cube/page/concave/zoom/linear/none
// Script dependencies to load
dependencies: []
// Stores if the next slide should be shown automatically
// after n milliseconds
autoSlide = config.autoSlide,

Hakim El Hattab
committed
// The horizontal and verical index of the currently active slide
indexh = 0,
indexv = 0,
// The previous and current slide HTML elements
previousSlide,
currentSlide,

Hakim El Hattab
committed
// Slides may hold a data-state attribute which we pick up and apply
// as a class to the body. This list contains the combined state of
// all current slides.
state = [],
dom = {},
// Detect support for CSS 3D transforms

hakimel
committed
supports3DTransforms = '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,
supports2DTransforms = '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,

Hakim El Hattab
committed
mouseWheelTimeout = 0,

Hakim El Hattab
committed
// An interval used to automatically move on to the next slide
autoSlideTimeout = 0,

Hakim El Hattab
committed
// Delays updates to the URL due to a Chrome thumbnailer bug
writeURLTimeout = 0,
// Holds information about the currently ongoing touch input
touch = {
startX: 0,
startY: 0,
startSpan: 0,
startCount: 0,
handled: false,
threshold: 80
* Starts up the presentation if the client is capable.
function initialize( options ) {
if( ( !supports2DTransforms && !supports3DTransforms ) ) {
document.body.setAttribute( 'class', 'no-transforms' );
// If the browser doesn't support core features we won't be
// using JavaScript to control the presentation
// Copy options over to our config object
extend( config, options );

Hakim El Hattab
committed
// Hide the address bar in mobile browsers
hideAddressBar();
// Loads the dependencies and continues to #start() once done
load();
}
/**
* Finds and stores references to DOM elements which are
* required by the presentation. If a required element is
* not found, it is created.
*/
function setupDOM() {
// Cache references to key DOM elements
dom.theme = document.querySelector( '#theme' );
dom.wrapper = document.querySelector( '.reveal' );

Hakim El Hattab
committed
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Progress bar
if( !dom.wrapper.querySelector( '.progress' ) && config.progress ) {
var progressElement = document.createElement( 'div' );
progressElement.classList.add( 'progress' );
progressElement.innerHTML = '<span></span>';
dom.wrapper.appendChild( progressElement );
}
// Arrow controls
if( !dom.wrapper.querySelector( '.controls' ) && config.controls ) {
var controlsElement = document.createElement( 'aside' );
controlsElement.classList.add( 'controls' );
controlsElement.innerHTML = '<a class="left" href="#">◄</a>' +
'<a class="right" href="#">►</a>' +
'<a class="up" href="#">▲</a>' +
'<a class="down" href="#">▼</a>';
dom.wrapper.appendChild( controlsElement );
}
// Presentation background element
if( !dom.wrapper.querySelector( '.state-background' ) ) {
var backgroundElement = document.createElement( 'div' );
backgroundElement.classList.add( 'state-background' );
dom.wrapper.appendChild( backgroundElement );
}
// Overlay graphic which is displayed during the paused mode
if( !dom.wrapper.querySelector( '.pause-overlay' ) ) {
var pausedElement = document.createElement( 'div' );
pausedElement.classList.add( 'pause-overlay' );
dom.wrapper.appendChild( pausedElement );
}
// Cache references to elements
dom.progress = document.querySelector( '.reveal .progress' );
dom.progressbar = document.querySelector( '.reveal .progress span' );
dom.controls = document.querySelector( '.reveal .controls' );
dom.controlsLeft = document.querySelector( '.reveal .controls .left' );
dom.controlsRight = document.querySelector( '.reveal .controls .right' );
dom.controlsUp = document.querySelector( '.reveal .controls .up' );
dom.controlsDown = document.querySelector( '.reveal .controls .down' );
}

Hakim El Hattab
committed
}

Hakim El Hattab
committed
/**
* Hides the address bar if we're on a mobile device.
*/
function hideAddressBar() {
if( navigator.userAgent.match( /(iphone|ipod|android)/i ) ) {
// Give the page some scrollable overflow
document.documentElement.style.overflow = 'scroll';
document.body.style.height = '120%';
// Events that should trigger the address bar to hide
window.addEventListener( 'load', removeAddressBar, false );
window.addEventListener( 'orientationchange', removeAddressBar, false );
}
}
/**
* Loads the dependencies of reveal.js. Dependencies are
* defined via the configuration option 'dependencies'
* and will be loaded prior to starting/binding reveal.js.
* Some dependencies may have an 'async' flag, if so they
* will load after reveal.js has been started up.
*/
function load() {
var scripts = [],
scriptsAsync = [];
for( var i = 0, len = config.dependencies.length; i < len; i++ ) {
var s = config.dependencies[i];
// Load if there's no condition or the condition is truthy
if( !s.condition || s.condition() ) {
if( s.async ) {
scriptsAsync.push( s.src );
}
else {
scripts.push( s.src );
}
// Extension may contain callback functions
if( typeof s.callback === 'function' ) {
head.ready( s.src.match( /([\w\d_\-]*)\.?[^\\\/]*$/i )[0], s.callback );
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
}
}
}
// Called once synchronous scritps finish loading
function proceed() {
// Load asynchronous scripts
head.js.apply( null, scriptsAsync );
start();
}
if( scripts.length ) {
head.ready( proceed );
// Load synchronous scripts
head.js.apply( null, scripts );
}
else {
proceed();
}
}
/**
* Starts up reveal.js by binding input events and navigating
* to the current URL deeplink if there is one.
*/
function start() {
// Make sure we've got all the DOM elements we need
setupDOM();
// Subscribe to input
addEventListeners();
// Updates the presentation to match the current configuration values
configure();
// Read the initial hash
readURL();
// Start auto-sliding if it's enabled
cueAutoSlide();
// Notify listeners that the presentation is ready
dispatchEvent( 'ready', {
'indexh': indexh,
'indexv': indexv,
'currentSlide': currentSlide
} );
}
/**
* Applies the configuration settings from the config object.
*/
function configure() {
if( supports3DTransforms === false ) {
config.transition = 'linear';
dom.controls.style.display = 'block';
}
if( config.progress && dom.progress ) {
dom.progress.style.display = 'block';
}
if( config.transition !== 'default' ) {
dom.wrapper.classList.add( config.transition );
document.addEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF
document.addEventListener( 'mousewheel', onDocumentMouseScroll, false );

Hakim El Hattab
committed
// 3D links
if( config.rollingLinks ) {
linkify();
}

Hakim El Hattab
committed
// Load the theme in the config, if it's not already loaded
if( config.theme && dom.theme ) {
var themeURL = dom.theme.getAttribute( 'href' );
var themeFinder = /[^\/]*?(?=\.css)/;
var themeName = themeURL.match(themeFinder)[0];
if( config.theme !== themeName ) {
themeURL = themeURL.replace(themeFinder, config.theme);
dom.theme.setAttribute( 'href', themeURL );
}
}

Hakim El Hattab
committed
/**
* Binds all event listeners.
*/
function addEventListeners() {
document.addEventListener( 'touchstart', onDocumentTouchStart, false );
document.addEventListener( 'touchmove', onDocumentTouchMove, false );
document.addEventListener( 'touchend', onDocumentTouchEnd, false );
window.addEventListener( 'hashchange', onWindowHashChange, false );
if( config.keyboard ) {
document.addEventListener( 'keydown', onDocumentKeyDown, false );
}
if ( config.progress && dom.progress ) {
dom.progress.addEventListener( 'click', preventAndForward( onProgressClick ), false );
}
dom.controlsLeft.addEventListener( 'click', preventAndForward( navigateLeft ), false );
dom.controlsRight.addEventListener( 'click', preventAndForward( navigateRight ), false );
dom.controlsUp.addEventListener( 'click', preventAndForward( navigateUp ), false );
dom.controlsDown.addEventListener( 'click', preventAndForward( navigateDown ), false );
}

Hakim El Hattab
committed
/**
* Unbinds all event listeners.
*/
function removeEventListeners() {
document.removeEventListener( 'keydown', onDocumentKeyDown, false );
document.removeEventListener( 'touchstart', onDocumentTouchStart, false );
document.removeEventListener( 'touchmove', onDocumentTouchMove, false );
document.removeEventListener( 'touchend', onDocumentTouchEnd, false );
window.removeEventListener( 'hashchange', onWindowHashChange, false );
if ( config.progress && dom.progress ) {
dom.progress.removeEventListener( 'click', preventAndForward( onProgressClick ), false );
}
dom.controlsLeft.removeEventListener( 'click', preventAndForward( navigateLeft ), false );
dom.controlsRight.removeEventListener( 'click', preventAndForward( navigateRight ), false );
dom.controlsUp.removeEventListener( 'click', preventAndForward( navigateUp ), false );
dom.controlsDown.removeEventListener( 'click', preventAndForward( navigateDown ), false );
}
/**
* Extend object a with the properties of object b.
* If there's a conflict, object b takes precedence.
*/
function extend( a, b ) {
for( var i in b ) {
a[ i ] = b[ i ];
}
}
/**
* Measures the distance in pixels between point a
* and point b.
*
* @param {Object} a point with x/y properties
* @param {Object} b point with x/y properties
*/
function distanceBetween( a, b ) {
var dx = a.x - b.x,
dy = a.y - b.y;
return Math.sqrt( dx*dx + dy*dy );
}
/**
* Prevents an events defaults behavior calls the
* specified delegate.
*
* @param {Function} delegate The method to call
* after the wrapper has been executed
*/
function preventAndForward( delegate ) {
return function( event ) {
event.preventDefault();
delegate.call( null, event );
/**
* Causes the address bar to hide on mobile devices,
* more vertical space ftw.
*/
function removeAddressBar() {
setTimeout( function() {
window.scrollTo( 0, 1 );
}, 0 );
}
/**
* Dispatches an event of the specified type from the
* reveal DOM element.
*/
function dispatchEvent( type, properties ) {
var event = document.createEvent( "HTMLEvents", 1, 2 );
event.initEvent( type, true, true );
extend( event, properties );
dom.wrapper.dispatchEvent( event );
}
/**
* Wrap all links in 3D goodness.
*/
function linkify() {

hakimel
committed
if( supports3DTransforms && !( 'msPerspective' in document.body.style ) ) {
var nodes = document.querySelectorAll( '.reveal .slides section a:not(.image)' );
for( var i = 0, len = nodes.length; i < len; i++ ) {
var node = nodes[i];
if( node.textContent && !node.querySelector( 'img' ) && ( !node.className || !node.classList.contains( node, 'roll' ) ) ) {
node.classList.add( 'roll' );
node.innerHTML = '<span data-title="'+ node.text +'">' + node.innerHTML + '</span>';
}
}
}
/**
* 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 ) {
dom.wrapper.classList.add( 'overview' );
var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR );
for( var i = 0, len1 = horizontalSlides.length; i < len1; i++ ) {
var hslide = horizontalSlides[i],
htransform = 'translateZ(-2500px) translate(' + ( ( i - indexh ) * 105 ) + '%, 0%)';
hslide.setAttribute( 'data-index-h', i );
hslide.style.display = 'block';
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' ) ) {
// Navigate to this slide on click
hslide.addEventListener( 'click', onOverviewSlideClicked, true );
}
var verticalSlides = hslide.querySelectorAll( 'section' );
for( var j = 0, len2 = verticalSlides.length; j < len2; j++ ) {
var vslide = verticalSlides[j],
vtransform = 'translate(0%, ' + ( ( j - ( i === indexh ? indexv : 0 ) ) * 105 ) + '%)';
vslide.setAttribute( 'data-index-h', i );
vslide.setAttribute( 'data-index-v', j );
vslide.style.display = 'block';
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 );
}
/**
* Exits the slide overview and enters the currently
* active slide.
*/
function deactivateOverview() {
// Only proceed if enabled in config
if( config.overview ) {
dom.wrapper.classList.remove( 'overview' );
// Select all slides
var slides = Array.prototype.slice.call( document.querySelectorAll( '.reveal .slides section' ) );
for( var i = 0, len = slides.length; i < len; i++ ) {
var element = slides[i];
// 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 );
}
slide();
}

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
* overview is open, false means it's closed.
*/
function toggleOverview( override ) {
if( typeof override === 'boolean' ) {
override ? activateOverview() : deactivateOverview();
}
else {
isOverviewActive() ? deactivateOverview() : activateOverview();
}
}
/**
* Checks if the overview is currently active.
*
* @return {Boolean} true if the overview is active,
* false otherwise
*/

Hakim El Hattab
committed
function isOverviewActive() {
return dom.wrapper.classList.contains( 'overview' );
/**
* 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() {
var element = document.body;
// Check which implementation is available
var requestMethod = element.requestFullScreen ||
element.webkitRequestFullScreen ||
element.mozRequestFullScreen ||
element.msRequestFullScreen;
if( requestMethod ) {
requestMethod.apply( element );
}
}

Hakim El Hattab
committed
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
/**
* Enters the paused mode which fades everything on screen to
* black.
*/
function pause() {
dom.wrapper.classList.add( 'paused' );
}
/**
* Exits from the paused mode.
*/
function resume() {
dom.wrapper.classList.remove( 'paused' );
}
/**
* Toggles the paused mode on and off.
*/
function togglePause() {
if( isPaused() ) {
resume();
}
else {
pause();
}
}
/**
* Checks if we are currently in the paused mode.
*/
function isPaused() {
return dom.wrapper.classList.contains( 'paused' );
}

Hakim El Hattab
committed

Hakim El Hattab
committed
* Steps from the current point in the presentation to the
* slide which matches the specified horizontal and vertical
* indices.
*
* @param {int} h Horizontal index of the target slide
* @param {int} v Vertical index of the target slide
function slide( h, v ) {

Hakim El Hattab
committed
// Remember where we were at before
previousSlide = currentSlide;
// 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
// 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 remaints of the previous state
while( stateBefore.length ) {
document.documentElement.classList.remove( stateBefore.pop() );

Hakim El Hattab
committed
}
// Update progress if enabled
if( config.progress && dom.progress ) {

Hakim El Hattab
committed
dom.progressbar.style.width = ( indexh / ( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ).length - 1 ) ) * window.innerWidth + 'px';
// If the overview is active, re-activate it to update positions

Hakim El Hattab
committed
if( isOverviewActive() ) {
activateOverview();
}
updateControls();
// Update the URL hash after a delay since updating it mid-transition
// is likely to cause visual lag

Hakim El Hattab
committed
clearTimeout( writeURLTimeout );
writeURLTimeout = setTimeout( writeURL, 1500 );

Hakim El Hattab
committed

Hakim El Hattab
committed
// Query all horizontal slides in the deck
var horizontalSlides = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR );

Hakim El Hattab
committed

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

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

Hakim El Hattab
committed
'indexv': indexv,

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

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
// stacks
if( previousSlide ) {
previousSlide.classList.remove( 'present' );
}

Hakim El Hattab
committed
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
/**
* Updates one dimension of slides by showing the slide
* with the specified index.
*
* @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
*
* @return {Number} The index of the slide that is now shown,
* might differ from the passed in index if it was out of
* bounds.
*/
function updateSlides( selector, index ) {
// Select all slides and convert the NodeList result to
// an array
var slides = Array.prototype.slice.call( document.querySelectorAll( selector ) ),
slidesLength = slides.length;
if( slidesLength ) {
// Should the index loop?
if( config.loop ) {
index %= slidesLength;
if( index < 0 ) {
index = slidesLength + index;
}
}
// Enforce max and minimum index bounds
index = Math.max( Math.min( index, slidesLength - 1 ), 0 );
for( var i = 0; i < slidesLength; i++ ) {
var element = slides[i];
// Optimization; hide all slides that are three or more steps
// away from the present slide
if( isOverviewActive() === false ) {
// The distance loops so that it measures 1 between the first
// and last slides
var distance = Math.abs( ( index - i ) % ( slidesLength - 3 ) ) || 0;
element.style.display = distance > 3 ? 'none' : 'block';
}
slides[i].classList.remove( 'past' );
slides[i].classList.remove( 'present' );
slides[i].classList.remove( 'future' );
if( i < index ) {
// Any element previous to index is given the 'past' class
slides[i].classList.add( 'past' );
}
else if( i > index ) {
// Any element subsequent to index is given the 'future' class
slides[i].classList.add( 'future' );
}
// If this element contains vertical slides
if( element.querySelector( 'section' ) ) {
slides[i].classList.add( 'stack' );
}
}
// Mark the current slide as present
slides[index].classList.add( 'present' );
// 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 attribtue associated use this as
// autoSlide value otherwise use the global configured time
var slideAutoSlide = slides[index].getAttribute( 'data-autoslide' );
if( slideAutoSlide ) {
autoSlide = parseInt( slideAutoSlide );
} else {
autoSlide = config.autoSlide
}
}
else {
// Since there are no slides we can't be anywhere beyond the
// zeroth index
index = 0;
}
return index;
}
/**
* Updates the state and link pointers of the controls.
*/
function updateControls() {

Hakim El Hattab
committed
if ( config.controls && dom.controls ) {
var routes = availableRoutes();
// Remove the 'enabled' class from all directions
[ dom.controlsLeft, dom.controlsRight, dom.controlsUp, dom.controlsDown ].forEach( function( node ) {
node.classList.remove( 'enabled' );
} );
// Add the 'enabled' class to the available routes
if( routes.left ) dom.controlsLeft.classList.add( 'enabled' );
if( routes.right ) dom.controlsRight.classList.add( 'enabled' );
if( routes.up ) dom.controlsUp.classList.add( 'enabled' );
if( routes.down ) dom.controlsDown.classList.add( 'enabled' );
}
/**
* 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 );
return {
left: indexh > 0,
right: indexh < horizontalSlides.length - 1,
up: indexv > 0,
down: indexv < verticalSlides.length - 1
};
}
/**
* 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

Hakim El Hattab
committed
slide( h, v );
}
/**
* Updates the page URL (hash) to reflect the current
if( config.history ) {
var url = '/';
// Only include the minimum possible number of components in
// the URL
if( indexh > 0 || indexv > 0 ) url += indexh;
if( indexv > 0 ) url += '/' + indexv;
window.location.hash = url;
}

Hakim El Hattab
committed
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
/**
* Retrieves the h/v location of the current, or specified,
* slide.
*
* @param {HTMLElement} slide If specified, the returned
* index will be for this slide rather than the currently
* active one
*
* @return {Object} { h: <int>, v: <int> }
*/
function getIndices( slide ) {
// By default, return the current indices
var h = indexh,
v = indexv;
// If a slide is specified, return the indices of that slide
if( slide ) {
var isVertical = !!slide.parentNode.nodeName.match( /section/gi );
var slideh = isVertical ? slide.parentNode : slide;
// Select all horizontal slides
var horizontalSlides = Array.prototype.slice.call( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
// 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( Array.prototype.slice.call( slide.parentNode.children ).indexOf( slide ), 0 );
}
}
return { h: h, v: v };
}
*
* @return {Boolean} true if there was a next fragment,
* false otherwise
*/
function nextFragment() {
// Vertical slides:
if( document.querySelector( VERTICAL_SLIDES_SELECTOR + '.present' ) ) {
var verticalFragments = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR + '.present .fragment:not(.visible)' );
if( verticalFragments.length ) {
verticalFragments[0].classList.add( 'visible' );

Hakim El Hattab
committed
// Notify subscribers of the change
dispatchEvent( 'fragmentshown', { fragment: verticalFragments[0] } );
return true;
}
}
else {
var horizontalFragments = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.present .fragment:not(.visible)' );
if( horizontalFragments.length ) {
horizontalFragments[0].classList.add( 'visible' );

Hakim El Hattab
committed
// Notify subscribers of the change
dispatchEvent( 'fragmentshown', { fragment: horizontalFragments[0] } );
}
return false;
}
/**
* Navigate to the previous slide fragment.
*
* @return {Boolean} true if there was a previous fragment,
* false otherwise
*/
function previousFragment() {
if( document.querySelector( VERTICAL_SLIDES_SELECTOR + '.present' ) ) {
var verticalFragments = document.querySelectorAll( VERTICAL_SLIDES_SELECTOR + '.present .fragment.visible' );
if( verticalFragments.length ) {
verticalFragments[ verticalFragments.length - 1 ].classList.remove( 'visible' );

Hakim El Hattab
committed
// Notify subscribers of the change
dispatchEvent( 'fragmenthidden', { fragment: verticalFragments[ verticalFragments.length - 1 ] } );
else {
var horizontalFragments = document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.present .fragment.visible' );
if( horizontalFragments.length ) {
horizontalFragments[ horizontalFragments.length - 1 ].classList.remove( 'visible' );

Hakim El Hattab
committed
// Notify subscribers of the change
dispatchEvent( 'fragmenthidden', { fragment: horizontalFragments[ horizontalFragments.length - 1 ] } );
return true;
}
}
return false;
}

Hakim El Hattab
committed