move fragments to separate controller
							parent
							
								
									bdedc56d6f
								
							
						
					
					
						commit
						8a43753000
					
				
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -0,0 +1,302 @@ | ||||||
|  | import { extend, toArray } from '../utils/util.js' | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | export default class Fragments { | ||||||
|  | 
 | ||||||
|  | 	constructor( Reveal ) { | ||||||
|  | 
 | ||||||
|  | 		this.Reveal = Reveal; | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Shows all fragments in the presentation. Used when | ||||||
|  | 	 * fragments are disabled presentation-wide. | ||||||
|  | 	 */ | ||||||
|  | 	showAll() { | ||||||
|  | 
 | ||||||
|  | 		toArray( this.Reveal.getSlidesElement().querySelectorAll( '.fragment' ) ).forEach( element => { | ||||||
|  | 			element.classList.add( 'visible' ); | ||||||
|  | 			element.classList.remove( 'current-fragment' ); | ||||||
|  | 		} ); | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Returns an object describing the available fragment | ||||||
|  | 	 * directions. | ||||||
|  | 	 * | ||||||
|  | 	 * @return {{prev: boolean, next: boolean}} | ||||||
|  | 	 */ | ||||||
|  | 	availableRoutes() { | ||||||
|  | 
 | ||||||
|  | 		let currentSlide = this.Reveal.getCurrentSlide(); | ||||||
|  | 		if( currentSlide && this.Reveal.getConfig().fragments ) { | ||||||
|  | 			let fragments = currentSlide.querySelectorAll( '.fragment' ); | ||||||
|  | 			let hiddenFragments = currentSlide.querySelectorAll( '.fragment:not(.visible)' ); | ||||||
|  | 
 | ||||||
|  | 			return { | ||||||
|  | 				prev: fragments.length - hiddenFragments.length > 0, | ||||||
|  | 				next: !!hiddenFragments.length | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 		else { | ||||||
|  | 			return { prev: false, next: false }; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Return a sorted fragments list, ordered by an increasing | ||||||
|  | 	 * "data-fragment-index" attribute. | ||||||
|  | 	 * | ||||||
|  | 	 * Fragments will be revealed in the order that they are returned by | ||||||
|  | 	 * this function, so you can use the index attributes to control the | ||||||
|  | 	 * order of fragment appearance. | ||||||
|  | 	 * | ||||||
|  | 	 * To maintain a sensible default fragment order, fragments are presumed | ||||||
|  | 	 * to be passed in document order. This function adds a "fragment-index" | ||||||
|  | 	 * attribute to each node if such an attribute is not already present, | ||||||
|  | 	 * and sets that attribute to an integer value which is the position of | ||||||
|  | 	 * the fragment within the fragments list. | ||||||
|  | 	 * | ||||||
|  | 	 * @param {object[]|*} fragments | ||||||
|  | 	 * @param {boolean} grouped If true the returned array will contain | ||||||
|  | 	 * nested arrays for all fragments with the same index | ||||||
|  | 	 * @return {object[]} sorted Sorted array of fragments | ||||||
|  | 	 */ | ||||||
|  | 	sort( fragments, grouped = false ) { | ||||||
|  | 
 | ||||||
|  | 		fragments = toArray( fragments ); | ||||||
|  | 
 | ||||||
|  | 		let ordered = [], | ||||||
|  | 			unordered = [], | ||||||
|  | 			sorted = []; | ||||||
|  | 
 | ||||||
|  | 		// Group ordered and unordered elements
 | ||||||
|  | 		fragments.forEach( fragment => { | ||||||
|  | 			if( fragment.hasAttribute( 'data-fragment-index' ) ) { | ||||||
|  | 				let index = parseInt( fragment.getAttribute( 'data-fragment-index' ), 10 ); | ||||||
|  | 
 | ||||||
|  | 				if( !ordered[index] ) { | ||||||
|  | 					ordered[index] = []; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				ordered[index].push( fragment ); | ||||||
|  | 			} | ||||||
|  | 			else { | ||||||
|  | 				unordered.push( [ fragment ] ); | ||||||
|  | 			} | ||||||
|  | 		} ); | ||||||
|  | 
 | ||||||
|  | 		// Append fragments without explicit indices in their
 | ||||||
|  | 		// DOM order
 | ||||||
|  | 		ordered = ordered.concat( unordered ); | ||||||
|  | 
 | ||||||
|  | 		// Manually count the index up per group to ensure there
 | ||||||
|  | 		// are no gaps
 | ||||||
|  | 		let index = 0; | ||||||
|  | 
 | ||||||
|  | 		// Push all fragments in their sorted order to an array,
 | ||||||
|  | 		// this flattens the groups
 | ||||||
|  | 		ordered.forEach( group => { | ||||||
|  | 			group.forEach( fragment => { | ||||||
|  | 				sorted.push( fragment ); | ||||||
|  | 				fragment.setAttribute( 'data-fragment-index', index ); | ||||||
|  | 			} ); | ||||||
|  | 
 | ||||||
|  | 			index ++; | ||||||
|  | 		} ); | ||||||
|  | 
 | ||||||
|  | 		return grouped === true ? ordered : sorted; | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Sorts and formats all of fragments in the | ||||||
|  | 	 * presentation. | ||||||
|  | 	 */ | ||||||
|  | 	sortAll() { | ||||||
|  | 
 | ||||||
|  | 		this.Reveal.getHorizontalSlides().forEach( horizontalSlide => { | ||||||
|  | 
 | ||||||
|  | 			let verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ); | ||||||
|  | 			verticalSlides.forEach( ( verticalSlide, y ) => { | ||||||
|  | 
 | ||||||
|  | 				this.sort( verticalSlide.querySelectorAll( '.fragment' ) ); | ||||||
|  | 
 | ||||||
|  | 			}, this ); | ||||||
|  | 
 | ||||||
|  | 			if( verticalSlides.length === 0 ) this.sort( horizontalSlide.querySelectorAll( '.fragment' ) ); | ||||||
|  | 
 | ||||||
|  | 		} ); | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Refreshes the fragments on the current slide so that they | ||||||
|  | 	 * have the appropriate classes (.visible + .current-fragment). | ||||||
|  | 	 * | ||||||
|  | 	 * @param {number} [index] The index of the current fragment | ||||||
|  | 	 * @param {array} [fragments] Array containing all fragments | ||||||
|  | 	 * in the current slide | ||||||
|  | 	 * | ||||||
|  | 	 * @return {{shown: array, hidden: array}} | ||||||
|  | 	 */ | ||||||
|  | 	update( index, fragments ) { | ||||||
|  | 
 | ||||||
|  | 		let changedFragments = { | ||||||
|  | 			shown: [], | ||||||
|  | 			hidden: [] | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		let currentSlide = this.Reveal.getCurrentSlide(); | ||||||
|  | 		if( currentSlide && this.Reveal.getConfig().fragments ) { | ||||||
|  | 
 | ||||||
|  | 			fragments = fragments || this.sort( currentSlide.querySelectorAll( '.fragment' ) ); | ||||||
|  | 
 | ||||||
|  | 			if( fragments.length ) { | ||||||
|  | 
 | ||||||
|  | 				let maxIndex = 0; | ||||||
|  | 
 | ||||||
|  | 				if( typeof index !== 'number' ) { | ||||||
|  | 					let currentFragment = this.sort( currentSlide.querySelectorAll( '.fragment.visible' ) ).pop(); | ||||||
|  | 					if( currentFragment ) { | ||||||
|  | 						index = parseInt( currentFragment.getAttribute( 'data-fragment-index' ) || 0, 10 ); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				toArray( fragments ).forEach( ( el, i ) => { | ||||||
|  | 
 | ||||||
|  | 					if( el.hasAttribute( 'data-fragment-index' ) ) { | ||||||
|  | 						i = parseInt( el.getAttribute( 'data-fragment-index' ), 10 ); | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					maxIndex = Math.max( maxIndex, i ); | ||||||
|  | 
 | ||||||
|  | 					// Visible fragments
 | ||||||
|  | 					if( i <= index ) { | ||||||
|  | 						if( !el.classList.contains( 'visible' ) ) changedFragments.shown.push( el ); | ||||||
|  | 						el.classList.add( 'visible' ); | ||||||
|  | 						el.classList.remove( 'current-fragment' ); | ||||||
|  | 
 | ||||||
|  | 						// Announce the fragments one by one to the Screen Reader
 | ||||||
|  | 						this.Reveal.announceStatus( this.Reveal.getStatusText( el ) ); | ||||||
|  | 
 | ||||||
|  | 						if( i === index ) { | ||||||
|  | 							el.classList.add( 'current-fragment' ); | ||||||
|  | 							this.Reveal.slideContent.startEmbeddedContent( el ); | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 					// Hidden fragments
 | ||||||
|  | 					else { | ||||||
|  | 						if( el.classList.contains( 'visible' ) ) changedFragments.hidden.push( el ); | ||||||
|  | 						el.classList.remove( 'visible' ); | ||||||
|  | 						el.classList.remove( 'current-fragment' ); | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 				} ); | ||||||
|  | 
 | ||||||
|  | 				// Write the current fragment index to the slide <section>.
 | ||||||
|  | 				// This can be used by end users to apply styles based on
 | ||||||
|  | 				// the current fragment index.
 | ||||||
|  | 				index = typeof index === 'number' ? index : -1; | ||||||
|  | 				index = Math.max( Math.min( index, maxIndex ), -1 ); | ||||||
|  | 				currentSlide.setAttribute( 'data-fragment', index ); | ||||||
|  | 
 | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return changedFragments; | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Navigate to the specified slide fragment. | ||||||
|  | 	 * | ||||||
|  | 	 * @param {?number} index The index of the fragment that | ||||||
|  | 	 * should be shown, -1 means all are invisible | ||||||
|  | 	 * @param {number} offset Integer offset to apply to the | ||||||
|  | 	 * fragment index | ||||||
|  | 	 * | ||||||
|  | 	 * @return {boolean} true if a change was made in any | ||||||
|  | 	 * fragments visibility as part of this call | ||||||
|  | 	 */ | ||||||
|  | 	goto( index, offset = 0 ) { | ||||||
|  | 
 | ||||||
|  | 		let currentSlide = this.Reveal.getCurrentSlide(); | ||||||
|  | 		if( currentSlide && this.Reveal.getConfig().fragments ) { | ||||||
|  | 
 | ||||||
|  | 			let fragments = this.sort( currentSlide.querySelectorAll( '.fragment' ) ); | ||||||
|  | 			if( fragments.length ) { | ||||||
|  | 
 | ||||||
|  | 				// If no index is specified, find the current
 | ||||||
|  | 				if( typeof index !== 'number' ) { | ||||||
|  | 					let lastVisibleFragment = this.sort( currentSlide.querySelectorAll( '.fragment.visible' ) ).pop(); | ||||||
|  | 
 | ||||||
|  | 					if( lastVisibleFragment ) { | ||||||
|  | 						index = parseInt( lastVisibleFragment.getAttribute( 'data-fragment-index' ) || 0, 10 ); | ||||||
|  | 					} | ||||||
|  | 					else { | ||||||
|  | 						index = -1; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// Apply the offset if there is one
 | ||||||
|  | 				index += offset; | ||||||
|  | 
 | ||||||
|  | 				let changedFragments = this.update( index, fragments ); | ||||||
|  | 
 | ||||||
|  | 				if( changedFragments.hidden.length ) { | ||||||
|  | 					this.Reveal.dispatchEvent( 'fragmenthidden', { fragment: changedFragments.hidden[0], fragments: changedFragments.hidden } ); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				if( changedFragments.shown.length ) { | ||||||
|  | 					this.Reveal.dispatchEvent( 'fragmentshown', { fragment: changedFragments.shown[0], fragments: changedFragments.shown } ); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				this.Reveal.updateControls(); | ||||||
|  | 				this.Reveal.updateProgress(); | ||||||
|  | 
 | ||||||
|  | 				if( this.Reveal.getConfig().fragmentInURL ) { | ||||||
|  | 					this.Reveal.writeURL(); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				return !!( changedFragments.shown.length || changedFragments.hidden.length ); | ||||||
|  | 
 | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return false; | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Navigate to the next slide fragment. | ||||||
|  | 	 * | ||||||
|  | 	 * @return {boolean} true if there was a next fragment, | ||||||
|  | 	 * false otherwise | ||||||
|  | 	 */ | ||||||
|  | 	next() { | ||||||
|  | 
 | ||||||
|  | 		return this.goto( null, 1 ); | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Navigate to the previous slide fragment. | ||||||
|  | 	 * | ||||||
|  | 	 * @return {boolean} true if there was a previous fragment, | ||||||
|  | 	 * false otherwise | ||||||
|  | 	 */ | ||||||
|  | 	prev() { | ||||||
|  | 
 | ||||||
|  | 		return this.goto( null, -1 ); | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| import { HORIZONTAL_SLIDES_SELECTOR, VERTICAL_SLIDES_SELECTOR } from '../utils/constants.js' | import { HORIZONTAL_SLIDES_SELECTOR, VERTICAL_SLIDES_SELECTOR } from '../utils/constants.js' | ||||||
| import { extend, toArray, closestParent } from '../utils/util.js' | import { extend, toArray, closestParent } from '../utils/util.js' | ||||||
|  | import { isMobile } from '../utils/device.js' | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Handles loading, unloading and playback of slide |  * Handles loading, unloading and playback of slide | ||||||
|  | @ -13,6 +14,26 @@ export default class SlideContent { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Should the given element be preloaded? | ||||||
|  | 	 * Decides based on local element attributes and global config. | ||||||
|  | 	 * | ||||||
|  | 	 * @param {HTMLElement} element | ||||||
|  | 	 */ | ||||||
|  | 	shouldPreload( element ) { | ||||||
|  | 
 | ||||||
|  | 		// Prefer an explicit global preload setting
 | ||||||
|  | 		let preload = this.Reveal.getConfig().preloadIframes; | ||||||
|  | 
 | ||||||
|  | 		// If no global setting is available, fall back on the element's
 | ||||||
|  | 		// own preload setting
 | ||||||
|  | 		if( typeof preload !== 'boolean' ) { | ||||||
|  | 			preload = element.hasAttribute( 'data-preload' ); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return preload; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Called when the given slide is within the configured view | 	 * Called when the given slide is within the configured view | ||||||
| 	 * distance. Shows the slide element and loads any content | 	 * distance. Shows the slide element and loads any content | ||||||
|  | @ -27,7 +48,7 @@ export default class SlideContent { | ||||||
| 
 | 
 | ||||||
| 		// Media elements with data-src attributes
 | 		// Media elements with data-src attributes
 | ||||||
| 		toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src], iframe[data-src]' ) ).forEach( element => { | 		toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src], iframe[data-src]' ) ).forEach( element => { | ||||||
| 			if( element.tagName !== 'IFRAME' || shouldPreload( element ) ) { | 			if( element.tagName !== 'IFRAME' || this.shouldPreload( element ) ) { | ||||||
| 				element.setAttribute( 'src', element.getAttribute( 'data-src' ) ); | 				element.setAttribute( 'src', element.getAttribute( 'data-src' ) ); | ||||||
| 				element.setAttribute( 'data-lazy-loaded', '' ); | 				element.setAttribute( 'data-lazy-loaded', '' ); | ||||||
| 				element.removeAttribute( 'data-src' ); | 				element.removeAttribute( 'data-src' ); | ||||||
|  | @ -89,7 +110,7 @@ export default class SlideContent { | ||||||
| 					// Inline video playback works (at least in Mobile Safari) as
 | 					// Inline video playback works (at least in Mobile Safari) as
 | ||||||
| 					// long as the video is muted and the `playsinline` attribute is
 | 					// long as the video is muted and the `playsinline` attribute is
 | ||||||
| 					// present
 | 					// present
 | ||||||
| 					if( isMobileDevice ) { | 					if( isMobile ) { | ||||||
| 						video.muted = true; | 						video.muted = true; | ||||||
| 						video.autoplay = true; | 						video.autoplay = true; | ||||||
| 						video.setAttribute( 'playsinline', '' ); | 						video.setAttribute( 'playsinline', '' ); | ||||||
|  | @ -126,7 +147,7 @@ export default class SlideContent { | ||||||
| 			if( backgroundIframeElement ) { | 			if( backgroundIframeElement ) { | ||||||
| 
 | 
 | ||||||
| 				// Check if this iframe is eligible to be preloaded
 | 				// Check if this iframe is eligible to be preloaded
 | ||||||
| 				if( shouldPreload( background ) && !/autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) { | 				if( this.shouldPreload( background ) && !/autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) { | ||||||
| 					if( backgroundIframeElement.getAttribute( 'src' ) !== backgroundIframe ) { | 					if( backgroundIframeElement.getAttribute( 'src' ) !== backgroundIframe ) { | ||||||
| 						backgroundIframeElement.setAttribute( 'src', backgroundIframe ); | 						backgroundIframeElement.setAttribute( 'src', backgroundIframe ); | ||||||
| 					} | 					} | ||||||
|  | @ -222,7 +243,7 @@ export default class SlideContent { | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				// Prefer an explicit global autoplay setting
 | 				// Prefer an explicit global autoplay setting
 | ||||||
| 				let autoplay = config.autoPlayMedia; | 				let autoplay = this.Reveal.getConfig().autoPlayMedia; | ||||||
| 
 | 
 | ||||||
| 				// If no global setting is available, fall back on the element's
 | 				// If no global setting is available, fall back on the element's
 | ||||||
| 				// own autoplay setting
 | 				// own autoplay setting
 | ||||||
|  | @ -238,7 +259,7 @@ export default class SlideContent { | ||||||
| 					} | 					} | ||||||
| 					// Mobile devices never fire a loaded event so instead
 | 					// Mobile devices never fire a loaded event so instead
 | ||||||
| 					// of waiting, we initiate playback
 | 					// of waiting, we initiate playback
 | ||||||
| 					else if( isMobileDevice ) { | 					else if( isMobile ) { | ||||||
| 						let promise = el.play(); | 						let promise = el.play(); | ||||||
| 
 | 
 | ||||||
| 						// If autoplay does not work, ensure that the controls are visible so
 | 						// If autoplay does not work, ensure that the controls are visible so
 | ||||||
|  | @ -327,7 +348,7 @@ export default class SlideContent { | ||||||
| 			if( isAttachedToDOM && isVisible ) { | 			if( isAttachedToDOM && isVisible ) { | ||||||
| 
 | 
 | ||||||
| 				// Prefer an explicit global autoplay setting
 | 				// Prefer an explicit global autoplay setting
 | ||||||
| 				let autoplay = config.autoPlayMedia; | 				let autoplay = this.Reveal.getConfig().autoPlayMedia; | ||||||
| 
 | 
 | ||||||
| 				// If no global setting is available, fall back on the element's
 | 				// If no global setting is available, fall back on the element's
 | ||||||
| 				// own autoplay setting
 | 				// own autoplay setting
 | ||||||
|  |  | ||||||
							
								
								
									
										453
									
								
								js/reveal.js
								
								
								
								
							
							
						
						
									
										453
									
								
								js/reveal.js
								
								
								
								
							|  | @ -1,5 +1,6 @@ | ||||||
| import SlideContent from './controllers/slidecontent.js' | import SlideContent from './controllers/slidecontent.js' | ||||||
| import AutoAnimate from './controllers/autoanimate.js' | import AutoAnimate from './controllers/autoanimate.js' | ||||||
|  | import Fragments from './controllers/fragments.js' | ||||||
| import Plugins from './controllers/plugins.js' | import Plugins from './controllers/plugins.js' | ||||||
| import Playback from './components/playback.js' | import Playback from './components/playback.js' | ||||||
| import defaultConfig from './config.js' | import defaultConfig from './config.js' | ||||||
|  | @ -19,6 +20,7 @@ import { | ||||||
| 	closestParent, | 	closestParent, | ||||||
| 	enterFullscreen | 	enterFullscreen | ||||||
| } from './utils/util.js' | } from './utils/util.js' | ||||||
|  | import { isMobile, isChrome, isAndroid, supportsZoom } from './utils/device.js' | ||||||
| import { colorToRgb, colorBrightness } from './utils/color.js' | import { colorToRgb, colorBrightness } from './utils/color.js' | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -88,18 +90,12 @@ export default function( revealElement, options ) { | ||||||
| 		// Controls auto-animations between slides
 | 		// Controls auto-animations between slides
 | ||||||
| 		autoAnimate = new AutoAnimate( Reveal ), | 		autoAnimate = new AutoAnimate( Reveal ), | ||||||
| 
 | 
 | ||||||
|  | 		// Controls navigation between slide fragments
 | ||||||
|  | 		fragments = new Fragments( Reveal ), | ||||||
|  | 
 | ||||||
| 		// List of asynchronously loaded reveal.js dependencies
 | 		// List of asynchronously loaded reveal.js dependencies
 | ||||||
| 		asyncDependencies = [], | 		asyncDependencies = [], | ||||||
| 
 | 
 | ||||||
| 		// Features supported by the browser, see #checkCapabilities()
 |  | ||||||
| 		features = {}, |  | ||||||
| 
 |  | ||||||
| 		// Client is a mobile device, see #checkCapabilities()
 |  | ||||||
| 		isMobileDevice, |  | ||||||
| 
 |  | ||||||
| 		// Client is a desktop Chrome, see #checkCapabilities()
 |  | ||||||
| 		isChrome, |  | ||||||
| 
 |  | ||||||
| 		// Throttles mouse wheel navigation
 | 		// Throttles mouse wheel navigation
 | ||||||
| 		lastMouseWheelStep = 0, | 		lastMouseWheelStep = 0, | ||||||
| 
 | 
 | ||||||
|  | @ -150,8 +146,6 @@ export default function( revealElement, options ) { | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		checkCapabilities(); |  | ||||||
| 
 |  | ||||||
| 		// Cache references to key DOM elements
 | 		// Cache references to key DOM elements
 | ||||||
| 		dom.wrapper = revealElement; | 		dom.wrapper = revealElement; | ||||||
| 		dom.slides = revealElement.querySelector( '.slides' ); | 		dom.slides = revealElement.querySelector( '.slides' ); | ||||||
|  | @ -169,25 +163,6 @@ export default function( revealElement, options ) { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * Inspect the client to see what features it supports. |  | ||||||
| 	 */ |  | ||||||
| 	function checkCapabilities() { |  | ||||||
| 
 |  | ||||||
| 		isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA ) || |  | ||||||
| 							( navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 ); // iPadOS
 |  | ||||||
| 		isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA ); |  | ||||||
| 
 |  | ||||||
| 		let testElement = document.createElement( 'div' ); |  | ||||||
| 
 |  | ||||||
| 		// Flags if we should use zoom instead of transform to scale
 |  | ||||||
| 		// up slides. Zoom produces crisper results but has a lot of
 |  | ||||||
| 		// xbrowser quirks so we only use it in whitelsited browsers.
 |  | ||||||
| 		features.zoom = 'zoom' in testElement.style && !isMobileDevice && |  | ||||||
| 						( isChrome || /Version\/[\d\.]+.*Safari/.test( UA ) ); |  | ||||||
| 
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Starts up reveal.js by binding input events and navigating | 	 * Starts up reveal.js by binding input events and navigating | ||||||
| 	 * to the current URL deeplink if there is one. | 	 * to the current URL deeplink if there is one. | ||||||
|  | @ -258,20 +233,13 @@ export default function( revealElement, options ) { | ||||||
| 		// Prevent transitions while we're loading
 | 		// Prevent transitions while we're loading
 | ||||||
| 		dom.slides.classList.add( 'no-transition' ); | 		dom.slides.classList.add( 'no-transition' ); | ||||||
| 
 | 
 | ||||||
| 		if( isMobileDevice ) { | 		if( isMobile ) { | ||||||
| 			dom.wrapper.classList.add( 'no-hover' ); | 			dom.wrapper.classList.add( 'no-hover' ); | ||||||
| 		} | 		} | ||||||
| 		else { | 		else { | ||||||
| 			dom.wrapper.classList.remove( 'no-hover' ); | 			dom.wrapper.classList.remove( 'no-hover' ); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if( /iphone/gi.test( UA ) ) { |  | ||||||
| 			dom.wrapper.classList.add( 'ua-iphone' ); |  | ||||||
| 		} |  | ||||||
| 		else { |  | ||||||
| 			dom.wrapper.classList.remove( 'ua-iphone' ); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Background element
 | 		// Background element
 | ||||||
| 		dom.background = createSingletonNode( dom.wrapper, 'div', 'backgrounds', null ); | 		dom.background = createSingletonNode( dom.wrapper, 'div', 'backgrounds', null ); | ||||||
| 
 | 
 | ||||||
|  | @ -341,6 +309,15 @@ export default function( revealElement, options ) { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Announces the given text to screen readers. | ||||||
|  | 	 */ | ||||||
|  | 	function announceStatus( value ) { | ||||||
|  | 
 | ||||||
|  | 		dom.statusDiv.textContent = value; | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Converts the given HTML element into a string of text | 	 * Converts the given HTML element into a string of text | ||||||
| 	 * that can be announced to a screen reader. Hidden | 	 * that can be announced to a screen reader. Hidden | ||||||
|  | @ -492,7 +469,7 @@ export default function( revealElement, options ) { | ||||||
| 					// Each fragment 'group' is an array containing one or more
 | 					// Each fragment 'group' is an array containing one or more
 | ||||||
| 					// fragments. Multiple fragments that appear at the same time
 | 					// fragments. Multiple fragments that appear at the same time
 | ||||||
| 					// are part of the same group.
 | 					// are part of the same group.
 | ||||||
| 					let fragmentGroups = sortFragments( page.querySelectorAll( '.fragment' ), true ); | 					let fragmentGroups = fragments.sort( page.querySelectorAll( '.fragment' ), true ); | ||||||
| 
 | 
 | ||||||
| 					let previousFragmentStep; | 					let previousFragmentStep; | ||||||
| 					let previousPage; | 					let previousPage; | ||||||
|  | @ -954,10 +931,7 @@ export default function( revealElement, options ) { | ||||||
| 
 | 
 | ||||||
| 		// When fragments are turned off they should be visible
 | 		// When fragments are turned off they should be visible
 | ||||||
| 		if( config.fragments === false ) { | 		if( config.fragments === false ) { | ||||||
| 			toArray( dom.slides.querySelectorAll( '.fragment' ) ).forEach( element => { | 			fragments.showAll(); | ||||||
| 				element.classList.add( 'visible' ); |  | ||||||
| 				element.classList.remove( 'current-fragment' ); |  | ||||||
| 			} ); |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Slide numbers
 | 		// Slide numbers
 | ||||||
|  | @ -1057,7 +1031,7 @@ export default function( revealElement, options ) { | ||||||
| 
 | 
 | ||||||
| 		// Only support touch for Android, fixes double navigations in
 | 		// Only support touch for Android, fixes double navigations in
 | ||||||
| 		// stock browser
 | 		// stock browser
 | ||||||
| 		if( UA.match( /android/gi ) ) { | 		if( isAndroid ) { | ||||||
| 			pointerEvents = [ 'touchstart' ]; | 			pointerEvents = [ 'touchstart' ]; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -1418,7 +1392,7 @@ export default function( revealElement, options ) { | ||||||
| 				// property where 100x adds up to the correct height.
 | 				// property where 100x adds up to the correct height.
 | ||||||
| 				//
 | 				//
 | ||||||
| 				// https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
 | 				// https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
 | ||||||
| 				if( isMobileDevice ) { | 				if( isMobile ) { | ||||||
| 					document.documentElement.style.setProperty( '--vh', ( window.innerHeight * 0.01 ) + 'px' ); | 					document.documentElement.style.setProperty( '--vh', ( window.innerHeight * 0.01 ) + 'px' ); | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
|  | @ -1454,7 +1428,7 @@ export default function( revealElement, options ) { | ||||||
| 					// effects are minor differences in text layout and iframe
 | 					// effects are minor differences in text layout and iframe
 | ||||||
| 					// viewports changing size. A 200x200 iframe viewport in a
 | 					// viewports changing size. A 200x200 iframe viewport in a
 | ||||||
| 					// 2x zoomed presentation ends up having a 400x400 viewport.
 | 					// 2x zoomed presentation ends up having a 400x400 viewport.
 | ||||||
| 					if( scale > 1 && features.zoom && window.devicePixelRatio < 2 ) { | 					if( scale > 1 && supportsZoom && window.devicePixelRatio < 2 ) { | ||||||
| 						dom.slides.style.zoom = scale; | 						dom.slides.style.zoom = scale; | ||||||
| 						dom.slides.style.left = ''; | 						dom.slides.style.left = ''; | ||||||
| 						dom.slides.style.top = ''; | 						dom.slides.style.top = ''; | ||||||
|  | @ -2060,7 +2034,7 @@ export default function( revealElement, options ) { | ||||||
| 
 | 
 | ||||||
| 		// Show fragment, if specified
 | 		// Show fragment, if specified
 | ||||||
| 		if( typeof f !== 'undefined' ) { | 		if( typeof f !== 'undefined' ) { | ||||||
| 			navigateFragment( f ); | 			fragments.goto( f ); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Dispatch an event if the slide changed
 | 		// Dispatch an event if the slide changed
 | ||||||
|  | @ -2126,8 +2100,8 @@ export default function( revealElement, options ) { | ||||||
| 			slideContent.startEmbeddedContent( currentSlide ); | 			slideContent.startEmbeddedContent( currentSlide ); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Announce the current slide contents, for screen readers
 | 		// Announce the current slide contents to screen readers
 | ||||||
| 		dom.statusDiv.textContent = getStatusText( currentSlide ); | 		announceStatus( getStatusText( currentSlide ) ); | ||||||
| 
 | 
 | ||||||
| 		updateControls(); | 		updateControls(); | ||||||
| 		updateProgress(); | 		updateProgress(); | ||||||
|  | @ -2135,7 +2109,8 @@ export default function( revealElement, options ) { | ||||||
| 		updateParallax(); | 		updateParallax(); | ||||||
| 		updateSlideNumber(); | 		updateSlideNumber(); | ||||||
| 		updateNotes(); | 		updateNotes(); | ||||||
| 		updateFragments(); | 
 | ||||||
|  | 		fragments.update(); | ||||||
| 
 | 
 | ||||||
| 		// Update the URL hash
 | 		// Update the URL hash
 | ||||||
| 		writeURL(); | 		writeURL(); | ||||||
|  | @ -2190,7 +2165,7 @@ export default function( revealElement, options ) { | ||||||
| 		// Write the current hash to the URL
 | 		// Write the current hash to the URL
 | ||||||
| 		writeURL(); | 		writeURL(); | ||||||
| 
 | 
 | ||||||
| 		sortAllFragments(); | 		fragments.sortAll(); | ||||||
| 
 | 
 | ||||||
| 		updateControls(); | 		updateControls(); | ||||||
| 		updateProgress(); | 		updateProgress(); | ||||||
|  | @ -2248,7 +2223,7 @@ export default function( revealElement, options ) { | ||||||
| 	 */ | 	 */ | ||||||
| 	function syncFragments( slide = currentSlide ) { | 	function syncFragments( slide = currentSlide ) { | ||||||
| 
 | 
 | ||||||
| 		return sortFragments( slide.querySelectorAll( '.fragment' ) ); | 		return config.sort( slide.querySelectorAll( '.fragment' ) ); | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -2275,27 +2250,6 @@ export default function( revealElement, options ) { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * Sorts and formats all of fragments in the |  | ||||||
| 	 * presentation. |  | ||||||
| 	 */ |  | ||||||
| 	function sortAllFragments() { |  | ||||||
| 
 |  | ||||||
| 		getHorizontalSlides().forEach( horizontalSlide => { |  | ||||||
| 
 |  | ||||||
| 			let verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ); |  | ||||||
| 			verticalSlides.forEach( ( verticalSlide, y ) => { |  | ||||||
| 
 |  | ||||||
| 				sortFragments( verticalSlide.querySelectorAll( '.fragment' ) ); |  | ||||||
| 
 |  | ||||||
| 			} ); |  | ||||||
| 
 |  | ||||||
| 			if( verticalSlides.length === 0 ) sortFragments( horizontalSlide.querySelectorAll( '.fragment' ) ); |  | ||||||
| 
 |  | ||||||
| 		} ); |  | ||||||
| 
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Randomly shuffles all slides in the deck. | 	 * Randomly shuffles all slides in the deck. | ||||||
| 	 */ | 	 */ | ||||||
|  | @ -2438,7 +2392,7 @@ export default function( revealElement, options ) { | ||||||
| 
 | 
 | ||||||
| 			// Shorten the view distance on devices that typically have
 | 			// Shorten the view distance on devices that typically have
 | ||||||
| 			// less resources
 | 			// less resources
 | ||||||
| 			if( isMobileDevice ) { | 			if( isMobile ) { | ||||||
| 				viewDistance = isOverview() ? 6 : config.mobileViewDistance; | 				viewDistance = isOverview() ? 6 : config.mobileViewDistance; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | @ -2657,7 +2611,7 @@ export default function( revealElement, options ) { | ||||||
| 	function updateControls() { | 	function updateControls() { | ||||||
| 
 | 
 | ||||||
| 		let routes = availableRoutes(); | 		let routes = availableRoutes(); | ||||||
| 		let fragments = availableFragments(); | 		let fragmentsRoutes = fragments.availableRoutes(); | ||||||
| 
 | 
 | ||||||
| 		// Remove the 'enabled' class from all directions
 | 		// Remove the 'enabled' class from all directions
 | ||||||
| 		[...dom.controlsLeft, ...dom.controlsRight, ...dom.controlsUp, ...dom.controlsDown, ...dom.controlsPrev, ...dom.controlsNext].forEach( node => { | 		[...dom.controlsLeft, ...dom.controlsRight, ...dom.controlsUp, ...dom.controlsDown, ...dom.controlsPrev, ...dom.controlsNext].forEach( node => { | ||||||
|  | @ -2681,23 +2635,22 @@ export default function( revealElement, options ) { | ||||||
| 		if( currentSlide ) { | 		if( currentSlide ) { | ||||||
| 
 | 
 | ||||||
| 			// Always apply fragment decorator to prev/next buttons
 | 			// Always apply fragment decorator to prev/next buttons
 | ||||||
| 			if( fragments.prev ) dom.controlsPrev.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); | 			if( fragmentsRoutes.prev ) dom.controlsPrev.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); | ||||||
| 			if( fragments.next ) dom.controlsNext.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); | 			if( fragmentsRoutes.next ) dom.controlsNext.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); | ||||||
| 
 | 
 | ||||||
| 			// Apply fragment decorators to directional buttons based on
 | 			// Apply fragment decorators to directional buttons based on
 | ||||||
| 			// what slide axis they are in
 | 			// what slide axis they are in
 | ||||||
| 			if( isVerticalSlide( currentSlide ) ) { | 			if( isVerticalSlide( currentSlide ) ) { | ||||||
| 				if( fragments.prev ) dom.controlsUp.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); | 				if( fragmentsRoutes.prev ) dom.controlsUp.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); | ||||||
| 				if( fragments.next ) dom.controlsDown.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); | 				if( fragmentsRoutes.next ) dom.controlsDown.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); | ||||||
| 			} | 			} | ||||||
| 			else { | 			else { | ||||||
| 				if( fragments.prev ) dom.controlsLeft.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); | 				if( fragmentsRoutes.prev ) dom.controlsLeft.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); | ||||||
| 				if( fragments.next ) dom.controlsRight.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); | 				if( fragmentsRoutes.next ) dom.controlsRight.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 		if( config.controlsTutorial ) { | 		if( config.controlsTutorial ) { | ||||||
| 
 | 
 | ||||||
| 			// Highlight control arrows with an animation to ensure
 | 			// Highlight control arrows with an animation to ensure
 | ||||||
|  | @ -2790,7 +2743,7 @@ export default function( revealElement, options ) { | ||||||
| 		// Stop content inside of previous backgrounds
 | 		// Stop content inside of previous backgrounds
 | ||||||
| 		if( previousBackground ) { | 		if( previousBackground ) { | ||||||
| 
 | 
 | ||||||
| 			slideContent.stopEmbeddedContent( previousBackground, { unloadIframes: !shouldPreload( previousBackground ) } ); | 			slideContent.stopEmbeddedContent( previousBackground, { unloadIframes: !slideContent.shouldPreload( previousBackground ) } ); | ||||||
| 
 | 
 | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -2901,26 +2854,6 @@ export default function( revealElement, options ) { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * Should the given element be preloaded? |  | ||||||
| 	 * Decides based on local element attributes and global config. |  | ||||||
| 	 * |  | ||||||
| 	 * @param {HTMLElement} element |  | ||||||
| 	 */ |  | ||||||
| 	function shouldPreload( element ) { |  | ||||||
| 
 |  | ||||||
| 		// Prefer an explicit global preload setting
 |  | ||||||
| 		let preload = config.preloadIframes; |  | ||||||
| 
 |  | ||||||
| 		// If no global setting is available, fall back on the element's
 |  | ||||||
| 		// own preload setting
 |  | ||||||
| 		if( typeof preload !== 'boolean' ) { |  | ||||||
| 			preload = element.hasAttribute( 'data-preload' ); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		return preload; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Determine what available routes there are for navigation. | 	 * Determine what available routes there are for navigation. | ||||||
| 	 * | 	 * | ||||||
|  | @ -2963,29 +2896,6 @@ export default function( revealElement, options ) { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * Returns an object describing the available fragment |  | ||||||
| 	 * directions. |  | ||||||
| 	 * |  | ||||||
| 	 * @return {{prev: boolean, next: boolean}} |  | ||||||
| 	 */ |  | ||||||
| 	function availableFragments() { |  | ||||||
| 
 |  | ||||||
| 		if( currentSlide && config.fragments ) { |  | ||||||
| 			let fragments = currentSlide.querySelectorAll( '.fragment' ); |  | ||||||
| 			let hiddenFragments = currentSlide.querySelectorAll( '.fragment:not(.visible)' ); |  | ||||||
| 
 |  | ||||||
| 			return { |  | ||||||
| 				prev: fragments.length - hiddenFragments.length > 0, |  | ||||||
| 				next: !!hiddenFragments.length |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
| 		else { |  | ||||||
| 			return { prev: false, next: false }; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Returns the number of past slides. This can be used as a global | 	 * Returns the number of past slides. This can be used as a global | ||||||
| 	 * flattened index for slides. | 	 * flattened index for slides. | ||||||
|  | @ -3433,234 +3343,6 @@ export default function( revealElement, options ) { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * Return a sorted fragments list, ordered by an increasing |  | ||||||
| 	 * "data-fragment-index" attribute. |  | ||||||
| 	 * |  | ||||||
| 	 * Fragments will be revealed in the order that they are returned by |  | ||||||
| 	 * this function, so you can use the index attributes to control the |  | ||||||
| 	 * order of fragment appearance. |  | ||||||
| 	 * |  | ||||||
| 	 * To maintain a sensible default fragment order, fragments are presumed |  | ||||||
| 	 * to be passed in document order. This function adds a "fragment-index" |  | ||||||
| 	 * attribute to each node if such an attribute is not already present, |  | ||||||
| 	 * and sets that attribute to an integer value which is the position of |  | ||||||
| 	 * the fragment within the fragments list. |  | ||||||
| 	 * |  | ||||||
| 	 * @param {object[]|*} fragments |  | ||||||
| 	 * @param {boolean} grouped If true the returned array will contain |  | ||||||
| 	 * nested arrays for all fragments with the same index |  | ||||||
| 	 * @return {object[]} sorted Sorted array of fragments |  | ||||||
| 	 */ |  | ||||||
| 	function sortFragments( fragments, grouped = false ) { |  | ||||||
| 
 |  | ||||||
| 		fragments = toArray( fragments ); |  | ||||||
| 
 |  | ||||||
| 		let ordered = [], |  | ||||||
| 			unordered = [], |  | ||||||
| 			sorted = []; |  | ||||||
| 
 |  | ||||||
| 		// Group ordered and unordered elements
 |  | ||||||
| 		fragments.forEach( fragment => { |  | ||||||
| 			if( fragment.hasAttribute( 'data-fragment-index' ) ) { |  | ||||||
| 				let index = parseInt( fragment.getAttribute( 'data-fragment-index' ), 10 ); |  | ||||||
| 
 |  | ||||||
| 				if( !ordered[index] ) { |  | ||||||
| 					ordered[index] = []; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				ordered[index].push( fragment ); |  | ||||||
| 			} |  | ||||||
| 			else { |  | ||||||
| 				unordered.push( [ fragment ] ); |  | ||||||
| 			} |  | ||||||
| 		} ); |  | ||||||
| 
 |  | ||||||
| 		// Append fragments without explicit indices in their
 |  | ||||||
| 		// DOM order
 |  | ||||||
| 		ordered = ordered.concat( unordered ); |  | ||||||
| 
 |  | ||||||
| 		// Manually count the index up per group to ensure there
 |  | ||||||
| 		// are no gaps
 |  | ||||||
| 		let index = 0; |  | ||||||
| 
 |  | ||||||
| 		// Push all fragments in their sorted order to an array,
 |  | ||||||
| 		// this flattens the groups
 |  | ||||||
| 		ordered.forEach( group => { |  | ||||||
| 			group.forEach( fragment => { |  | ||||||
| 				sorted.push( fragment ); |  | ||||||
| 				fragment.setAttribute( 'data-fragment-index', index ); |  | ||||||
| 			} ); |  | ||||||
| 
 |  | ||||||
| 			index ++; |  | ||||||
| 		} ); |  | ||||||
| 
 |  | ||||||
| 		return grouped === true ? ordered : sorted; |  | ||||||
| 
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * Refreshes the fragments on the current slide so that they |  | ||||||
| 	 * have the appropriate classes (.visible + .current-fragment). |  | ||||||
| 	 * |  | ||||||
| 	 * @param {number} [index] The index of the current fragment |  | ||||||
| 	 * @param {array} [fragments] Array containing all fragments |  | ||||||
| 	 * in the current slide |  | ||||||
| 	 * |  | ||||||
| 	 * @return {{shown: array, hidden: array}} |  | ||||||
| 	 */ |  | ||||||
| 	function updateFragments( index, fragments ) { |  | ||||||
| 
 |  | ||||||
| 		let changedFragments = { |  | ||||||
| 			shown: [], |  | ||||||
| 			hidden: [] |  | ||||||
| 		}; |  | ||||||
| 
 |  | ||||||
| 		if( currentSlide && config.fragments ) { |  | ||||||
| 
 |  | ||||||
| 			fragments = fragments || sortFragments( currentSlide.querySelectorAll( '.fragment' ) ); |  | ||||||
| 
 |  | ||||||
| 			if( fragments.length ) { |  | ||||||
| 
 |  | ||||||
| 				let maxIndex = 0; |  | ||||||
| 
 |  | ||||||
| 				if( typeof index !== 'number' ) { |  | ||||||
| 					let currentFragment = sortFragments( currentSlide.querySelectorAll( '.fragment.visible' ) ).pop(); |  | ||||||
| 					if( currentFragment ) { |  | ||||||
| 						index = parseInt( currentFragment.getAttribute( 'data-fragment-index' ) || 0, 10 ); |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				toArray( fragments ).forEach( ( el, i ) => { |  | ||||||
| 
 |  | ||||||
| 					if( el.hasAttribute( 'data-fragment-index' ) ) { |  | ||||||
| 						i = parseInt( el.getAttribute( 'data-fragment-index' ), 10 ); |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					maxIndex = Math.max( maxIndex, i ); |  | ||||||
| 
 |  | ||||||
| 					// Visible fragments
 |  | ||||||
| 					if( i <= index ) { |  | ||||||
| 						if( !el.classList.contains( 'visible' ) ) changedFragments.shown.push( el ); |  | ||||||
| 						el.classList.add( 'visible' ); |  | ||||||
| 						el.classList.remove( 'current-fragment' ); |  | ||||||
| 
 |  | ||||||
| 						// Announce the fragments one by one to the Screen Reader
 |  | ||||||
| 						dom.statusDiv.textContent = getStatusText( el ); |  | ||||||
| 
 |  | ||||||
| 						if( i === index ) { |  | ||||||
| 							el.classList.add( 'current-fragment' ); |  | ||||||
| 							slideContent.startEmbeddedContent( el ); |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 					// Hidden fragments
 |  | ||||||
| 					else { |  | ||||||
| 						if( el.classList.contains( 'visible' ) ) changedFragments.hidden.push( el ); |  | ||||||
| 						el.classList.remove( 'visible' ); |  | ||||||
| 						el.classList.remove( 'current-fragment' ); |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 				} ); |  | ||||||
| 
 |  | ||||||
| 				// Write the current fragment index to the slide <section>.
 |  | ||||||
| 				// This can be used by end users to apply styles based on
 |  | ||||||
| 				// the current fragment index.
 |  | ||||||
| 				index = typeof index === 'number' ? index : -1; |  | ||||||
| 				index = Math.max( Math.min( index, maxIndex ), -1 ); |  | ||||||
| 				currentSlide.setAttribute( 'data-fragment', index ); |  | ||||||
| 
 |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		return changedFragments; |  | ||||||
| 
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * Navigate to the specified slide fragment. |  | ||||||
| 	 * |  | ||||||
| 	 * @param {?number} index The index of the fragment that |  | ||||||
| 	 * should be shown, -1 means all are invisible |  | ||||||
| 	 * @param {number} offset Integer offset to apply to the |  | ||||||
| 	 * fragment index |  | ||||||
| 	 * |  | ||||||
| 	 * @return {boolean} true if a change was made in any |  | ||||||
| 	 * fragments visibility as part of this call |  | ||||||
| 	 */ |  | ||||||
| 	function navigateFragment( index, offset = 0 ) { |  | ||||||
| 
 |  | ||||||
| 		if( currentSlide && config.fragments ) { |  | ||||||
| 
 |  | ||||||
| 			let fragments = sortFragments( currentSlide.querySelectorAll( '.fragment' ) ); |  | ||||||
| 			if( fragments.length ) { |  | ||||||
| 
 |  | ||||||
| 				// If no index is specified, find the current
 |  | ||||||
| 				if( typeof index !== 'number' ) { |  | ||||||
| 					let lastVisibleFragment = sortFragments( currentSlide.querySelectorAll( '.fragment.visible' ) ).pop(); |  | ||||||
| 
 |  | ||||||
| 					if( lastVisibleFragment ) { |  | ||||||
| 						index = parseInt( lastVisibleFragment.getAttribute( 'data-fragment-index' ) || 0, 10 ); |  | ||||||
| 					} |  | ||||||
| 					else { |  | ||||||
| 						index = -1; |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				// Apply the offset if there is one
 |  | ||||||
| 				index += offset; |  | ||||||
| 
 |  | ||||||
| 				let changedFragments = updateFragments( index, fragments ); |  | ||||||
| 
 |  | ||||||
| 				if( changedFragments.hidden.length ) { |  | ||||||
| 					dispatchEvent( 'fragmenthidden', { fragment: changedFragments.hidden[0], fragments: changedFragments.hidden } ); |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				if( changedFragments.shown.length ) { |  | ||||||
| 					dispatchEvent( 'fragmentshown', { fragment: changedFragments.shown[0], fragments: changedFragments.shown } ); |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				updateControls(); |  | ||||||
| 				updateProgress(); |  | ||||||
| 
 |  | ||||||
| 				if( config.fragmentInURL ) { |  | ||||||
| 					writeURL(); |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				return !!( changedFragments.shown.length || changedFragments.hidden.length ); |  | ||||||
| 
 |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		return false; |  | ||||||
| 
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * Navigate to the next slide fragment. |  | ||||||
| 	 * |  | ||||||
| 	 * @return {boolean} true if there was a next fragment, |  | ||||||
| 	 * false otherwise |  | ||||||
| 	 */ |  | ||||||
| 	function nextFragment() { |  | ||||||
| 
 |  | ||||||
| 		return navigateFragment( null, 1 ); |  | ||||||
| 
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * Navigate to the previous slide fragment. |  | ||||||
| 	 * |  | ||||||
| 	 * @return {boolean} true if there was a previous fragment, |  | ||||||
| 	 * false otherwise |  | ||||||
| 	 */ |  | ||||||
| 	function prevFragment() { |  | ||||||
| 
 |  | ||||||
| 		return navigateFragment( null, -1 ); |  | ||||||
| 
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Cues a new automated slide if enabled in the config. | 	 * Cues a new automated slide if enabled in the config. | ||||||
| 	 */ | 	 */ | ||||||
|  | @ -3719,7 +3401,7 @@ export default function( revealElement, options ) { | ||||||
| 			// - The presentation isn't paused
 | 			// - The presentation isn't paused
 | ||||||
| 			// - The overview isn't active
 | 			// - The overview isn't active
 | ||||||
| 			// - The presentation isn't over
 | 			// - The presentation isn't over
 | ||||||
| 			if( autoSlide && !autoSlidePaused && !isPaused() && !isOverview() && ( !Reveal.isLastSlide() || availableFragments().next || config.loop === true ) ) { | 			if( autoSlide && !autoSlidePaused && !isPaused() && !isOverview() && ( !Reveal.isLastSlide() || fragments.availableRoutes().next || config.loop === true ) ) { | ||||||
| 				autoSlideTimeout = setTimeout( () => { | 				autoSlideTimeout = setTimeout( () => { | ||||||
| 					if( typeof config.autoSlideMethod === 'function' ) { | 					if( typeof config.autoSlideMethod === 'function' ) { | ||||||
| 						config.autoSlideMethod() | 						config.autoSlideMethod() | ||||||
|  | @ -3780,12 +3462,12 @@ export default function( revealElement, options ) { | ||||||
| 
 | 
 | ||||||
| 		// Reverse for RTL
 | 		// Reverse for RTL
 | ||||||
| 		if( config.rtl ) { | 		if( config.rtl ) { | ||||||
| 			if( ( isOverview() || nextFragment() === false ) && availableRoutes().left ) { | 			if( ( isOverview() || fragments.next() === false ) && availableRoutes().left ) { | ||||||
| 				slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined ); | 				slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined ); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		// Normal navigation
 | 		// Normal navigation
 | ||||||
| 		else if( ( isOverview() || prevFragment() === false ) && availableRoutes().left ) { | 		else if( ( isOverview() || fragments.prev() === false ) && availableRoutes().left ) { | ||||||
| 			slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined ); | 			slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined ); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -3797,12 +3479,12 @@ export default function( revealElement, options ) { | ||||||
| 
 | 
 | ||||||
| 		// Reverse for RTL
 | 		// Reverse for RTL
 | ||||||
| 		if( config.rtl ) { | 		if( config.rtl ) { | ||||||
| 			if( ( isOverview() || prevFragment() === false ) && availableRoutes().right ) { | 			if( ( isOverview() || fragments.prev() === false ) && availableRoutes().right ) { | ||||||
| 				slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined ); | 				slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined ); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		// Normal navigation
 | 		// Normal navigation
 | ||||||
| 		else if( ( isOverview() || nextFragment() === false ) && availableRoutes().right ) { | 		else if( ( isOverview() || fragments.next() === false ) && availableRoutes().right ) { | ||||||
| 			slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined ); | 			slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined ); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -3811,7 +3493,7 @@ export default function( revealElement, options ) { | ||||||
| 	function navigateUp() { | 	function navigateUp() { | ||||||
| 
 | 
 | ||||||
| 		// Prioritize hiding fragments
 | 		// Prioritize hiding fragments
 | ||||||
| 		if( ( isOverview() || prevFragment() === false ) && availableRoutes().up ) { | 		if( ( isOverview() || fragments.prev() === false ) && availableRoutes().up ) { | ||||||
| 			slide( indexh, indexv - 1 ); | 			slide( indexh, indexv - 1 ); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -3822,7 +3504,7 @@ export default function( revealElement, options ) { | ||||||
| 		hasNavigatedVertically = true; | 		hasNavigatedVertically = true; | ||||||
| 
 | 
 | ||||||
| 		// Prioritize revealing fragments
 | 		// Prioritize revealing fragments
 | ||||||
| 		if( ( isOverview() || nextFragment() === false ) && availableRoutes().down ) { | 		if( ( isOverview() || fragments.next() === false ) && availableRoutes().down ) { | ||||||
| 			slide( indexh, indexv + 1 ); | 			slide( indexh, indexv + 1 ); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -3837,7 +3519,7 @@ export default function( revealElement, options ) { | ||||||
| 	function navigatePrev() { | 	function navigatePrev() { | ||||||
| 
 | 
 | ||||||
| 		// Prioritize revealing fragments
 | 		// Prioritize revealing fragments
 | ||||||
| 		if( prevFragment() === false ) { | 		if( fragments.prev() === false ) { | ||||||
| 			if( availableRoutes().up ) { | 			if( availableRoutes().up ) { | ||||||
| 				navigateUp(); | 				navigateUp(); | ||||||
| 			} | 			} | ||||||
|  | @ -3871,7 +3553,7 @@ export default function( revealElement, options ) { | ||||||
| 		hasNavigatedVertically = true; | 		hasNavigatedVertically = true; | ||||||
| 
 | 
 | ||||||
| 		// Prioritize revealing fragments
 | 		// Prioritize revealing fragments
 | ||||||
| 		if( nextFragment() === false ) { | 		if( fragments.next() === false ) { | ||||||
| 
 | 
 | ||||||
| 			let routes = availableRoutes(); | 			let routes = availableRoutes(); | ||||||
| 
 | 
 | ||||||
|  | @ -4293,7 +3975,7 @@ export default function( revealElement, options ) { | ||||||
| 		} | 		} | ||||||
| 		// There's a bug with swiping on some Android devices unless
 | 		// There's a bug with swiping on some Android devices unless
 | ||||||
| 		// the default action is always prevented
 | 		// the default action is always prevented
 | ||||||
| 		else if( UA.match( /android/gi ) ) { | 		else if( isAndroid ) { | ||||||
| 			event.preventDefault(); | 			event.preventDefault(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -4562,9 +4244,9 @@ export default function( revealElement, options ) { | ||||||
| 		navigateNext: navigateNext, | 		navigateNext: navigateNext, | ||||||
| 
 | 
 | ||||||
| 		// Fragment methods
 | 		// Fragment methods
 | ||||||
| 		navigateFragment, | 		navigateFragment: () => fragments.goto, | ||||||
| 		prevFragment, | 		prevFragment: () => fragments.prev, | ||||||
| 		nextFragment, | 		nextFragment: () => fragments.next, | ||||||
| 
 | 
 | ||||||
| 		// Forces an update in slide layout
 | 		// Forces an update in slide layout
 | ||||||
| 		layout, | 		layout, | ||||||
|  | @ -4576,7 +4258,7 @@ export default function( revealElement, options ) { | ||||||
| 		availableRoutes, | 		availableRoutes, | ||||||
| 
 | 
 | ||||||
| 		// Returns an object with the available fragments as booleans (prev/next)
 | 		// Returns an object with the available fragments as booleans (prev/next)
 | ||||||
| 		availableFragments, | 		availableFragments: () => fragments.availableRoutes(), | ||||||
| 
 | 
 | ||||||
| 		// Toggles a help overlay with keyboard shortcuts
 | 		// Toggles a help overlay with keyboard shortcuts
 | ||||||
| 		toggleHelp, | 		toggleHelp, | ||||||
|  | @ -4650,6 +4332,20 @@ export default function( revealElement, options ) { | ||||||
| 		addKeyBinding, | 		addKeyBinding, | ||||||
| 		removeKeyBinding, | 		removeKeyBinding, | ||||||
| 
 | 
 | ||||||
|  | 		// Programmatically triggers a keyboard event
 | ||||||
|  | 		triggerKey: keyCode => onDocumentKeyDown( { keyCode } ), | ||||||
|  | 
 | ||||||
|  | 		// Registers a new shortcut to include in the help overlay
 | ||||||
|  | 		registerKeyboardShortcut: ( key, value ) => keyboardShortcuts[key] = value, | ||||||
|  | 
 | ||||||
|  | 		// Forward event binding to the reveal DOM element
 | ||||||
|  | 		addEventListener: ( type, listener, useCapture ) => { | ||||||
|  | 			Reveal.getRevealElement().addEventListener( type, listener, useCapture ); | ||||||
|  | 		}, | ||||||
|  | 		removeEventListener: ( type, listener, useCapture ) => { | ||||||
|  | 			Reveal.getRevealElement().removeEventListener( type, listener, useCapture ); | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
| 		// API for registering and retrieving plugins
 | 		// API for registering and retrieving plugins
 | ||||||
| 		registerPlugin: (...args) => plugins.registerPlugin( ...args ), | 		registerPlugin: (...args) => plugins.registerPlugin( ...args ), | ||||||
| 		hasPlugin: (...args) => plugins.hasPlugin( ...args ), | 		hasPlugin: (...args) => plugins.hasPlugin( ...args ), | ||||||
|  | @ -4732,19 +4428,18 @@ export default function( revealElement, options ) { | ||||||
| 		// Checks if reveal.js has been loaded and is ready for use
 | 		// Checks if reveal.js has been loaded and is ready for use
 | ||||||
| 		isReady: () => ready, | 		isReady: () => ready, | ||||||
| 
 | 
 | ||||||
| 		// Forward event binding to the reveal DOM element
 |  | ||||||
| 		addEventListener: ( type, listener, useCapture ) => { |  | ||||||
| 			Reveal.getRevealElement().addEventListener( type, listener, useCapture ); |  | ||||||
| 		}, |  | ||||||
| 		removeEventListener: ( type, listener, useCapture ) => { |  | ||||||
| 			Reveal.getRevealElement().removeEventListener( type, listener, useCapture ); |  | ||||||
| 		}, |  | ||||||
| 
 | 
 | ||||||
| 		// Programmatically triggers a keyboard event
 | 		// Methods for announcing content to screen readers
 | ||||||
| 		triggerKey: keyCode => onDocumentKeyDown( { keyCode } ), | 		announceStatus, | ||||||
|  | 		getStatusText, | ||||||
|  | 
 | ||||||
|  | 		// Expose direct access to controllers via the API
 | ||||||
|  | 		slideContent, | ||||||
|  | 
 | ||||||
|  | 		updateControls, | ||||||
|  | 		updateProgress, | ||||||
|  | 		writeURL | ||||||
| 
 | 
 | ||||||
| 		// Registers a new shortcut to include in the help overlay
 |  | ||||||
| 		registerKeyboardShortcut: ( key, value ) => keyboardShortcuts[key] = value |  | ||||||
| 	} ); | 	} ); | ||||||
| 
 | 
 | ||||||
| }; | }; | ||||||
|  | @ -0,0 +1,15 @@ | ||||||
|  | const UA = navigator.userAgent; | ||||||
|  | const testElement = document.createElement( 'div' ); | ||||||
|  | 
 | ||||||
|  | export const isMobile = /(iphone|ipod|ipad|android)/gi.test( UA ) || | ||||||
|  | 						( navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 ); // iPadOS
 | ||||||
|  | 
 | ||||||
|  | export const isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA ); | ||||||
|  | 
 | ||||||
|  | export const isAndroid = /android/gi.test( UA ); | ||||||
|  | 
 | ||||||
|  | // Flags if we should use zoom instead of transform to scale
 | ||||||
|  | // up slides. Zoom produces crisper results but has a lot of
 | ||||||
|  | // xbrowser quirks so we only use it in whitelsited browsers.
 | ||||||
|  | export const supportsZoom = 'zoom' in testElement.style && !isMobile && | ||||||
|  | 				( isChrome || /Version\/[\d\.]+.*Safari/.test( UA ) ); | ||||||
							
								
								
									
										614
									
								
								test/test.html
								
								
								
								
							
							
						
						
									
										614
									
								
								test/test.html
								
								
								
								
							|  | @ -78,7 +78,619 @@ | ||||||
| 		</div> | 		</div> | ||||||
| 
 | 
 | ||||||
| 		<script src="../dist/reveal.min.js"></script> | 		<script src="../dist/reveal.min.js"></script> | ||||||
| 		<script src="test.js"></script> | 		<script> | ||||||
|  | 			// These tests expect the DOM to contain a presentation | ||||||
|  | 			// with the following slide structure: | ||||||
|  | 			// | ||||||
|  | 			// 1 | ||||||
|  | 			// 2 - Three sub-slides | ||||||
|  | 			// 3 - Three fragment elements | ||||||
|  | 			// 3 - Two fragments with same data-fragment-index | ||||||
|  | 			// 4 | ||||||
|  | 			Reveal.initialize().then( function() { | ||||||
|  | 
 | ||||||
|  | 				// --------------------------------------------------------------- | ||||||
|  | 				// DOM TESTS | ||||||
|  | 
 | ||||||
|  | 				QUnit.module( 'DOM' ); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Initial slides classes', function( assert ) { | ||||||
|  | 					var horizontalSlides = document.querySelectorAll( '.reveal .slides>section' ) | ||||||
|  | 
 | ||||||
|  | 					assert.strictEqual( document.querySelectorAll( '.reveal .slides section.past' ).length, 0, 'no .past slides' ); | ||||||
|  | 					assert.strictEqual( document.querySelectorAll( '.reveal .slides section.present' ).length, 1, 'one .present slide' ); | ||||||
|  | 					assert.strictEqual( document.querySelectorAll( '.reveal .slides>section.future' ).length, horizontalSlides.length - 1, 'remaining horizontal slides are .future' ); | ||||||
|  | 
 | ||||||
|  | 					assert.strictEqual( document.querySelectorAll( '.reveal .slides section.stack' ).length, 2, 'two .stacks' ); | ||||||
|  | 
 | ||||||
|  | 					assert.ok( document.querySelectorAll( '.reveal .slides section.stack' )[0].querySelectorAll( '.future' ).length > 0, 'vertical slides are given .future' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				// --------------------------------------------------------------- | ||||||
|  | 				// API TESTS | ||||||
|  | 
 | ||||||
|  | 				QUnit.module( 'API' ); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.isReady', function( assert ) { | ||||||
|  | 					assert.strictEqual( Reveal.isReady(), true, 'returns true' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.isOverview', function( assert ) { | ||||||
|  | 					assert.strictEqual( Reveal.isOverview(), false, 'false by default' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.toggleOverview(); | ||||||
|  | 					assert.strictEqual( Reveal.isOverview(), true, 'true after toggling on' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.toggleOverview(); | ||||||
|  | 					assert.strictEqual( Reveal.isOverview(), false, 'false after toggling off' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.isPaused', function( assert ) { | ||||||
|  | 					assert.strictEqual( Reveal.isPaused(), false, 'false by default' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.togglePause(); | ||||||
|  | 					assert.strictEqual( Reveal.isPaused(), true, 'true after pausing' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.togglePause(); | ||||||
|  | 					assert.strictEqual( Reveal.isPaused(), false, 'false after resuming' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.isFirstSlide', function( assert ) { | ||||||
|  | 					Reveal.slide( 0, 0 ); | ||||||
|  | 					assert.strictEqual( Reveal.isFirstSlide(), true, 'true after Reveal.slide( 0, 0 )' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 1, 0 ); | ||||||
|  | 					assert.strictEqual( Reveal.isFirstSlide(), false, 'false after Reveal.slide( 1, 0 )' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 0, 0 ); | ||||||
|  | 					assert.strictEqual( Reveal.isFirstSlide(), true, 'true after Reveal.slide( 0, 0 )' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.isFirstSlide after vertical slide', function( assert ) { | ||||||
|  | 					Reveal.slide( 1, 1 ); | ||||||
|  | 					Reveal.slide( 0, 0 ); | ||||||
|  | 					assert.strictEqual( Reveal.isFirstSlide(), true, 'true after Reveal.slide( 1, 1 ) and then Reveal.slide( 0, 0 )' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.isLastSlide', function( assert ) { | ||||||
|  | 					Reveal.slide( 0, 0 ); | ||||||
|  | 					assert.strictEqual( Reveal.isLastSlide(), false, 'false after Reveal.slide( 0, 0 )' ); | ||||||
|  | 
 | ||||||
|  | 					var lastSlideIndex = document.querySelectorAll( '.reveal .slides>section' ).length - 1; | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( lastSlideIndex, 0 ); | ||||||
|  | 					assert.strictEqual( Reveal.isLastSlide(), true, 'true after Reveal.slide( '+ lastSlideIndex +', 0 )' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 0, 0 ); | ||||||
|  | 					assert.strictEqual( Reveal.isLastSlide(), false, 'false after Reveal.slide( 0, 0 )' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.isLastSlide after vertical slide', function( assert ) { | ||||||
|  | 					var lastSlideIndex = document.querySelectorAll( '.reveal .slides>section' ).length - 1; | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 1, 1 ); | ||||||
|  | 					Reveal.slide( lastSlideIndex ); | ||||||
|  | 					assert.strictEqual( Reveal.isLastSlide(), true, 'true after Reveal.slide( 1, 1 ) and then Reveal.slide( '+ lastSlideIndex +', 0 )' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.getTotalSlides', function( assert ) { | ||||||
|  | 					assert.strictEqual( Reveal.getTotalSlides(), 8, 'eight slides in total' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.getIndices', function( assert ) { | ||||||
|  | 					var indices = Reveal.getIndices(); | ||||||
|  | 
 | ||||||
|  | 					assert.ok( indices.hasOwnProperty( 'h' ), 'h exists' ); | ||||||
|  | 					assert.ok( indices.hasOwnProperty( 'v' ), 'v exists' ); | ||||||
|  | 					assert.ok( indices.hasOwnProperty( 'f' ), 'f exists' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 1, 0 ); | ||||||
|  | 					assert.strictEqual( Reveal.getIndices().h, 1, 'h 1' ); | ||||||
|  | 					assert.strictEqual( Reveal.getIndices().v, 0, 'v 0' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 1, 2 ); | ||||||
|  | 					assert.strictEqual( Reveal.getIndices().h, 1, 'h 1' ); | ||||||
|  | 					assert.strictEqual( Reveal.getIndices().v, 2, 'v 2' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 0, 0 ); | ||||||
|  | 					assert.strictEqual( Reveal.getIndices().h, 0, 'h 0' ); | ||||||
|  | 					assert.strictEqual( Reveal.getIndices().v, 0, 'v 0' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.getSlide', function( assert ) { | ||||||
|  | 					assert.equal( Reveal.getSlide( 0 ), document.querySelector( '.reveal .slides>section:first-child' ), 'gets correct first slide' ); | ||||||
|  | 					assert.equal( Reveal.getSlide( 1 ), document.querySelector( '.reveal .slides>section:nth-child(2)' ), 'no v index returns stack' ); | ||||||
|  | 					assert.equal( Reveal.getSlide( 1, 0 ), document.querySelector( '.reveal .slides>section:nth-child(2)>section:nth-child(1)' ), 'v index 0 returns first vertical child' ); | ||||||
|  | 					assert.equal( Reveal.getSlide( 1, 1 ), document.querySelector( '.reveal .slides>section:nth-child(2)>section:nth-child(2)' ), 'v index 1 returns second vertical child' ); | ||||||
|  | 
 | ||||||
|  | 					assert.strictEqual( Reveal.getSlide( 100 ), undefined, 'undefined when out of horizontal bounds' ); | ||||||
|  | 					assert.strictEqual( Reveal.getSlide( 1, 100 ), undefined, 'undefined when out of vertical bounds' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.getSlideBackground', function( assert ) { | ||||||
|  | 					assert.equal( Reveal.getSlideBackground( 0 ), document.querySelector( '.reveal .backgrounds>.slide-background:first-child' ), 'gets correct first background' ); | ||||||
|  | 					assert.equal( Reveal.getSlideBackground( 1 ), document.querySelector( '.reveal .backgrounds>.slide-background:nth-child(2)' ), 'no v index returns stack' ); | ||||||
|  | 					assert.equal( Reveal.getSlideBackground( 1, 0 ), document.querySelector( '.reveal .backgrounds>.slide-background:nth-child(2) .slide-background:nth-child(2)' ), 'v index 0 returns first vertical child' ); | ||||||
|  | 					assert.equal( Reveal.getSlideBackground( 1, 1 ), document.querySelector( '.reveal .backgrounds>.slide-background:nth-child(2) .slide-background:nth-child(3)' ), 'v index 1 returns second vertical child' ); | ||||||
|  | 
 | ||||||
|  | 					assert.strictEqual( Reveal.getSlideBackground( 100 ), undefined, 'undefined when out of horizontal bounds' ); | ||||||
|  | 					assert.strictEqual( Reveal.getSlideBackground( 1, 100 ), undefined, 'undefined when out of vertical bounds' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.getSlideNotes', function( assert ) { | ||||||
|  | 					Reveal.slide( 0, 0 ); | ||||||
|  | 					assert.ok( Reveal.getSlideNotes() === 'speaker notes 1', 'works with <aside class="notes">' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 1, 0 ); | ||||||
|  | 					assert.ok( Reveal.getSlideNotes() === 'speaker notes 2', 'works with <section data-notes="">' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.getPreviousSlide/getCurrentSlide', function( assert ) { | ||||||
|  | 					Reveal.slide( 0, 0 ); | ||||||
|  | 					Reveal.slide( 1, 0 ); | ||||||
|  | 
 | ||||||
|  | 					var firstSlide = document.querySelector( '.reveal .slides>section:first-child' ); | ||||||
|  | 					var secondSlide = document.querySelector( '.reveal .slides>section:nth-child(2)>section' ); | ||||||
|  | 
 | ||||||
|  | 					assert.equal( Reveal.getPreviousSlide(), firstSlide, 'previous is slide #0' ); | ||||||
|  | 					assert.equal( Reveal.getCurrentSlide(), secondSlide, 'current is slide #1' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.getProgress', function( assert ) { | ||||||
|  | 					Reveal.slide( 0, 0 ); | ||||||
|  | 					assert.strictEqual( Reveal.getProgress(), 0, 'progress is 0 on first slide' ); | ||||||
|  | 
 | ||||||
|  | 					var lastSlideIndex = document.querySelectorAll( '.reveal .slides>section' ).length - 1; | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( lastSlideIndex, 0 ); | ||||||
|  | 					assert.strictEqual( Reveal.getProgress(), 1, 'progress is 1 on last slide' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.getScale', function( assert ) { | ||||||
|  | 					assert.ok( typeof Reveal.getScale() === 'number', 'has scale' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.getConfig', function( assert ) { | ||||||
|  | 					assert.ok( typeof Reveal.getConfig() === 'object', 'has config' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.configure', function( assert ) { | ||||||
|  | 					assert.strictEqual( Reveal.getConfig().loop, false, '"loop" is false to start with' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.configure({ loop: true }); | ||||||
|  | 					assert.strictEqual( Reveal.getConfig().loop, true, '"loop" has changed to true' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.configure({ loop: false, customTestValue: 1 }); | ||||||
|  | 					assert.strictEqual( Reveal.getConfig().customTestValue, 1, 'supports custom values' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.availableRoutes', function( assert ) { | ||||||
|  | 					Reveal.slide( 0, 0 ); | ||||||
|  | 					assert.deepEqual( Reveal.availableRoutes(), { left: false, up: false, down: false, right: true }, 'correct for first slide' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 1, 0 ); | ||||||
|  | 					assert.deepEqual( Reveal.availableRoutes(), { left: true, up: false, down: true, right: true }, 'correct for vertical slide' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.next', function( assert ) { | ||||||
|  | 					Reveal.slide( 0, 0 ); | ||||||
|  | 
 | ||||||
|  | 					// Step through vertical child slides | ||||||
|  | 					Reveal.next(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 1, v: 0, f: undefined } ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.next(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 1, v: 1, f: undefined } ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.next(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 1, v: 2, f: undefined } ); | ||||||
|  | 
 | ||||||
|  | 					// Step through fragments | ||||||
|  | 					Reveal.next(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: -1 } ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.next(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 } ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.next(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 1 } ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.next(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 2 } ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.next at end', function( assert ) { | ||||||
|  | 					Reveal.slide( 3 ); | ||||||
|  | 
 | ||||||
|  | 					// We're at the end, this should have no effect | ||||||
|  | 					Reveal.next(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 3, v: 0, f: undefined } ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.next(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 3, v: 0, f: undefined } ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 				// --------------------------------------------------------------- | ||||||
|  | 				// FRAGMENT TESTS | ||||||
|  | 
 | ||||||
|  | 				QUnit.module( 'Fragments' ); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Sliding to fragments', function( assert ) { | ||||||
|  | 					Reveal.slide( 2, 0, -1 ); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: -1 }, 'Reveal.slide( 2, 0, -1 )' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 2, 0, 0 ); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 }, 'Reveal.slide( 2, 0, 0 )' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 2, 0, 2 ); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 2 }, 'Reveal.slide( 2, 0, 2 )' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 2, 0, 1 ); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 1 }, 'Reveal.slide( 2, 0, 1 )' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'data-fragment is set on slide <section>', function( assert ) { | ||||||
|  | 					Reveal.slide( 2, 0, -1 ); | ||||||
|  | 					assert.deepEqual( Reveal.getCurrentSlide().getAttribute( 'data-fragment' ), '-1' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 2, 0, 2 ); | ||||||
|  | 					assert.deepEqual( Reveal.getCurrentSlide().getAttribute( 'data-fragment' ), '2' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 2, 0, 0 ); | ||||||
|  | 					assert.deepEqual( Reveal.getCurrentSlide().getAttribute( 'data-fragment' ), '0' ); | ||||||
|  | 
 | ||||||
|  | 					var fragmentSlide = Reveal.getCurrentSlide(); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 3, 0 ); | ||||||
|  | 					assert.deepEqual( fragmentSlide.getAttribute( 'data-fragment' ), '0', 'data-fragment persists when jumping to another slide' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Hiding all fragments', function( assert ) { | ||||||
|  | 					var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(1)' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 2, 0, 0 ); | ||||||
|  | 					assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 1, 'one fragment visible when index is 0' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 2, 0, -1 ); | ||||||
|  | 					assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 0, 'no fragments visible when index is -1' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Current fragment', function( assert ) { | ||||||
|  | 					var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(1)' ); | ||||||
|  | 					var lastFragmentIndex = [].slice.call( fragmentSlide.querySelectorAll( '.fragment' ) ).pop().getAttribute( 'data-fragment-index' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 2, 0 ); | ||||||
|  | 					assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 0, 'no current fragment at index -1' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 2, 0, 0 ); | ||||||
|  | 					assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 1, 'one current fragment at index 0' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 1, 0, 0 ); | ||||||
|  | 					assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 0, 'no current fragment when navigating to previous slide' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 3, 0, 0 ); | ||||||
|  | 					assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 0, 'no current fragment when navigating to next slide' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 2, 1, -1 ); | ||||||
|  | 					Reveal.prev(); | ||||||
|  | 					assert.strictEqual( fragmentSlide.querySelector( '.fragment.current-fragment' ).getAttribute( 'data-fragment-index' ), lastFragmentIndex, 'last fragment is current fragment when returning from future slide' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Stepping through fragments', function( assert ) { | ||||||
|  | 					Reveal.slide( 2, 0, -1 ); | ||||||
|  | 
 | ||||||
|  | 					// forwards: | ||||||
|  | 
 | ||||||
|  | 					Reveal.next(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 }, 'next() goes to next fragment' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.right(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 1 }, 'right() goes to next fragment' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.down(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 2 }, 'down() goes to next fragment' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.down(); // moves to f #3 | ||||||
|  | 
 | ||||||
|  | 					// backwards: | ||||||
|  | 
 | ||||||
|  | 					Reveal.prev(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 2 }, 'prev() goes to prev fragment' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.left(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 1 }, 'left() goes to prev fragment' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.up(); | ||||||
|  | 					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 }, 'up() goes to prev fragment' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Stepping past fragments', function( assert ) { | ||||||
|  | 					var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(1)' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 0, 0, 0 ); | ||||||
|  | 					assert.equal( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 0, 'no fragments visible when on previous slide' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 3, 0, 0 ); | ||||||
|  | 					assert.equal( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 3, 'all fragments visible when on future slide' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Fragment indices', function( assert ) { | ||||||
|  | 					var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(2)' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 3, 0, 0 ); | ||||||
|  | 					assert.equal( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 2, 'both fragments of same index are shown' ); | ||||||
|  | 
 | ||||||
|  | 					// This slide has three fragments, first one is index 0, second and third have index 1 | ||||||
|  | 					Reveal.slide( 2, 2, 0 ); | ||||||
|  | 					assert.equal( Reveal.getIndices().f, 0, 'returns correct index for first fragment' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 2, 2, 1 ); | ||||||
|  | 					assert.equal( Reveal.getIndices().f, 1, 'returns correct index for two fragments with same index' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Index generation', function( assert ) { | ||||||
|  | 					var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(1)' ); | ||||||
|  | 
 | ||||||
|  | 					// These have no indices defined to start with | ||||||
|  | 					assert.equal( fragmentSlide.querySelectorAll( '.fragment' )[0].getAttribute( 'data-fragment-index' ), '0' ); | ||||||
|  | 					assert.equal( fragmentSlide.querySelectorAll( '.fragment' )[1].getAttribute( 'data-fragment-index' ), '1' ); | ||||||
|  | 					assert.equal( fragmentSlide.querySelectorAll( '.fragment' )[2].getAttribute( 'data-fragment-index' ), '2' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Index normalization', function( assert ) { | ||||||
|  | 					var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(3)' ); | ||||||
|  | 
 | ||||||
|  | 					// These start out as 1-4-4 and should normalize to 0-1-1 | ||||||
|  | 					assert.equal( fragmentSlide.querySelectorAll( '.fragment' )[0].getAttribute( 'data-fragment-index' ), '0' ); | ||||||
|  | 					assert.equal( fragmentSlide.querySelectorAll( '.fragment' )[1].getAttribute( 'data-fragment-index' ), '1' ); | ||||||
|  | 					assert.equal( fragmentSlide.querySelectorAll( '.fragment' )[2].getAttribute( 'data-fragment-index' ), '1' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'fragmentshown event', function( assert ) { | ||||||
|  | 					assert.expect( 2 ); | ||||||
|  | 					var done = assert.async( 2 ); | ||||||
|  | 
 | ||||||
|  | 					var _onEvent = function( event ) { | ||||||
|  | 						assert.ok( true, 'event fired' ); | ||||||
|  | 						done(); | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					Reveal.addEventListener( 'fragmentshown', _onEvent ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 2, 0 ); | ||||||
|  | 					Reveal.slide( 2, 0 ); // should do nothing | ||||||
|  | 					Reveal.slide( 2, 0, 0 ); // should do nothing | ||||||
|  | 					Reveal.next(); | ||||||
|  | 					Reveal.next(); | ||||||
|  | 					Reveal.prev(); // shouldn't fire fragmentshown | ||||||
|  | 
 | ||||||
|  | 					Reveal.removeEventListener( 'fragmentshown', _onEvent ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'fragmenthidden event', function( assert ) { | ||||||
|  | 					assert.expect( 2 ); | ||||||
|  | 					var done = assert.async( 2 ); | ||||||
|  | 
 | ||||||
|  | 					var _onEvent = function( event ) { | ||||||
|  | 						assert.ok( true, 'event fired' ); | ||||||
|  | 						done(); | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					Reveal.addEventListener( 'fragmenthidden', _onEvent ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 2, 0, 2 ); | ||||||
|  | 					Reveal.slide( 2, 0, 2 ); // should do nothing | ||||||
|  | 					Reveal.prev(); | ||||||
|  | 					Reveal.prev(); | ||||||
|  | 					Reveal.next(); // shouldn't fire fragmenthidden | ||||||
|  | 
 | ||||||
|  | 					Reveal.removeEventListener( 'fragmenthidden', _onEvent ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 				// --------------------------------------------------------------- | ||||||
|  | 				// AUTO-SLIDE TESTS | ||||||
|  | 
 | ||||||
|  | 				QUnit.module( 'Auto Sliding' ); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.isAutoSliding', function( assert ) { | ||||||
|  | 					assert.strictEqual( Reveal.isAutoSliding(), false, 'false by default' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.configure({ autoSlide: 10000 }); | ||||||
|  | 					assert.strictEqual( Reveal.isAutoSliding(), true, 'true after starting' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.configure({ autoSlide: 0 }); | ||||||
|  | 					assert.strictEqual( Reveal.isAutoSliding(), false, 'false after setting to 0' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Reveal.toggleAutoSlide', function( assert ) { | ||||||
|  | 					Reveal.configure({ autoSlide: 10000 }); | ||||||
|  | 
 | ||||||
|  | 					Reveal.toggleAutoSlide(); | ||||||
|  | 					assert.strictEqual( Reveal.isAutoSliding(), false, 'false after first toggle' ); | ||||||
|  | 					Reveal.toggleAutoSlide(); | ||||||
|  | 					assert.strictEqual( Reveal.isAutoSliding(), true, 'true after second toggle' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.configure({ autoSlide: 0 }); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'autoslidepaused', function( assert ) { | ||||||
|  | 					assert.expect( 1 ); | ||||||
|  | 					var done = assert.async(); | ||||||
|  | 
 | ||||||
|  | 					var _onEvent = function( event ) { | ||||||
|  | 						assert.ok( true, 'event fired' ); | ||||||
|  | 						done(); | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					Reveal.addEventListener( 'autoslidepaused', _onEvent ); | ||||||
|  | 					Reveal.configure({ autoSlide: 10000 }); | ||||||
|  | 					Reveal.toggleAutoSlide(); | ||||||
|  | 
 | ||||||
|  | 					// cleanup | ||||||
|  | 					Reveal.configure({ autoSlide: 0 }); | ||||||
|  | 					Reveal.removeEventListener( 'autoslidepaused', _onEvent ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'autoslideresumed', function( assert ) { | ||||||
|  | 					assert.expect( 1 ); | ||||||
|  | 					var done = assert.async(); | ||||||
|  | 
 | ||||||
|  | 					var _onEvent = function( event ) { | ||||||
|  | 						assert.ok( true, 'event fired' ); | ||||||
|  | 						done(); | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					Reveal.addEventListener( 'autoslideresumed', _onEvent ); | ||||||
|  | 					Reveal.configure({ autoSlide: 10000 }); | ||||||
|  | 					Reveal.toggleAutoSlide(); | ||||||
|  | 					Reveal.toggleAutoSlide(); | ||||||
|  | 
 | ||||||
|  | 					// cleanup | ||||||
|  | 					Reveal.configure({ autoSlide: 0 }); | ||||||
|  | 					Reveal.removeEventListener( 'autoslideresumed', _onEvent ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 				// --------------------------------------------------------------- | ||||||
|  | 				// CONFIGURATION VALUES | ||||||
|  | 
 | ||||||
|  | 				QUnit.module( 'Configuration' ); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Controls', function( assert ) { | ||||||
|  | 					var controlsElement = document.querySelector( '.reveal>.controls' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.configure({ controls: false }); | ||||||
|  | 					assert.equal( controlsElement.style.display, 'none', 'controls are hidden' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.configure({ controls: true }); | ||||||
|  | 					assert.equal( controlsElement.style.display, 'block', 'controls are visible' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Progress', function( assert ) { | ||||||
|  | 					var progressElement = document.querySelector( '.reveal>.progress' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.configure({ progress: false }); | ||||||
|  | 					assert.equal( progressElement.style.display, 'none', 'progress are hidden' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.configure({ progress: true }); | ||||||
|  | 					assert.equal( progressElement.style.display, 'block', 'progress are visible' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'Loop', function( assert ) { | ||||||
|  | 					Reveal.configure({ loop: true }); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 0, 0 ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.left(); | ||||||
|  | 					assert.notEqual( Reveal.getIndices().h, 0, 'looped from start to end' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.right(); | ||||||
|  | 					assert.equal( Reveal.getIndices().h, 0, 'looped from end to start' ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.configure({ loop: false }); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 				// --------------------------------------------------------------- | ||||||
|  | 				// LAZY-LOADING TESTS | ||||||
|  | 
 | ||||||
|  | 				QUnit.module( 'Lazy-Loading' ); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'img with data-src', function( assert ) { | ||||||
|  | 					assert.strictEqual( document.querySelectorAll( '.reveal section img[src]' ).length, 1, 'Image source has been set' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'video with data-src', function( assert ) { | ||||||
|  | 					assert.strictEqual( document.querySelectorAll( '.reveal section video[src]' ).length, 1, 'Video source has been set' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'audio with data-src', function( assert ) { | ||||||
|  | 					assert.strictEqual( document.querySelectorAll( '.reveal section audio[src]' ).length, 1, 'Audio source has been set' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'iframe with data-src', function( assert ) { | ||||||
|  | 					Reveal.slide( 0, 0 ); | ||||||
|  | 					assert.strictEqual( document.querySelectorAll( '.reveal section iframe[src]' ).length, 0, 'Iframe source is not set' ); | ||||||
|  | 					Reveal.slide( 2, 1 ); | ||||||
|  | 					assert.strictEqual( document.querySelectorAll( '.reveal section iframe[src]' ).length, 1, 'Iframe source is set' ); | ||||||
|  | 					Reveal.slide( 2, 2 ); | ||||||
|  | 					assert.strictEqual( document.querySelectorAll( '.reveal section iframe[src]' ).length, 0, 'Iframe source is not set' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'background images', function( assert ) { | ||||||
|  | 					var imageSource1 = Reveal.getSlide( 0 ).getAttribute( 'data-background-image' ); | ||||||
|  | 					var imageSource2 = Reveal.getSlide( 1, 0 ).getAttribute( 'data-background' ); | ||||||
|  | 
 | ||||||
|  | 					// check that the images are applied to the background elements | ||||||
|  | 					assert.ok( Reveal.getSlideBackground( 0 ).querySelector( '.slide-background-content' ).style.backgroundImage.indexOf( imageSource1 ) !== -1, 'data-background-image worked' ); | ||||||
|  | 					assert.ok( Reveal.getSlideBackground( 1, 0 ).querySelector( '.slide-background-content' ).style.backgroundImage.indexOf( imageSource2 ) !== -1, 'data-background worked' ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 				// --------------------------------------------------------------- | ||||||
|  | 				// EVENT TESTS | ||||||
|  | 
 | ||||||
|  | 				QUnit.module( 'Events' ); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'slidechanged', function( assert ) { | ||||||
|  | 					assert.expect( 3 ); | ||||||
|  | 					var done = assert.async( 3 ); | ||||||
|  | 
 | ||||||
|  | 					var _onEvent = function( event ) { | ||||||
|  | 						assert.ok( true, 'event fired' ); | ||||||
|  | 						done(); | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					Reveal.addEventListener( 'slidechanged', _onEvent ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.slide( 1, 0 ); // should trigger | ||||||
|  | 					Reveal.slide( 1, 0 ); // should do nothing | ||||||
|  | 					Reveal.next(); // should trigger | ||||||
|  | 					Reveal.slide( 3, 0 ); // should trigger | ||||||
|  | 					Reveal.next(); // should do nothing | ||||||
|  | 
 | ||||||
|  | 					Reveal.removeEventListener( 'slidechanged', _onEvent ); | ||||||
|  | 
 | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'paused', function( assert ) { | ||||||
|  | 					assert.expect( 1 ); | ||||||
|  | 					var done = assert.async(); | ||||||
|  | 
 | ||||||
|  | 					var _onEvent = function( event ) { | ||||||
|  | 						assert.ok( true, 'event fired' ); | ||||||
|  | 						done(); | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					Reveal.addEventListener( 'paused', _onEvent ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.togglePause(); | ||||||
|  | 					Reveal.togglePause(); | ||||||
|  | 
 | ||||||
|  | 					Reveal.removeEventListener( 'paused', _onEvent ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 				QUnit.test( 'resumed', function( assert ) { | ||||||
|  | 					assert.expect( 1 ); | ||||||
|  | 					var done = assert.async(); | ||||||
|  | 
 | ||||||
|  | 					var _onEvent = function( event ) { | ||||||
|  | 						assert.ok( true, 'event fired' ); | ||||||
|  | 						done(); | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					Reveal.addEventListener( 'resumed', _onEvent ); | ||||||
|  | 
 | ||||||
|  | 					Reveal.togglePause(); | ||||||
|  | 					Reveal.togglePause(); | ||||||
|  | 
 | ||||||
|  | 					Reveal.removeEventListener( 'resumed', _onEvent ); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 			} ); | ||||||
|  | 		</script> | ||||||
| 
 | 
 | ||||||
| 	</body> | 	</body> | ||||||
| </html> | </html> | ||||||
|  |  | ||||||
							
								
								
									
										612
									
								
								test/test.js
								
								
								
								
							
							
						
						
									
										612
									
								
								test/test.js
								
								
								
								
							|  | @ -1,612 +0,0 @@ | ||||||
| // These tests expect the DOM to contain a presentation
 |  | ||||||
| // with the following slide structure:
 |  | ||||||
| //
 |  | ||||||
| // 1
 |  | ||||||
| // 2 - Three sub-slides
 |  | ||||||
| // 3 - Three fragment elements
 |  | ||||||
| // 3 - Two fragments with same data-fragment-index
 |  | ||||||
| // 4
 |  | ||||||
| 
 |  | ||||||
| Reveal.initialize().then( function() { |  | ||||||
| 
 |  | ||||||
| 	// ---------------------------------------------------------------
 |  | ||||||
| 	// DOM TESTS
 |  | ||||||
| 
 |  | ||||||
| 	QUnit.module( 'DOM' ); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Initial slides classes', function( assert ) { |  | ||||||
| 		var horizontalSlides = document.querySelectorAll( '.reveal .slides>section' ) |  | ||||||
| 
 |  | ||||||
| 		assert.strictEqual( document.querySelectorAll( '.reveal .slides section.past' ).length, 0, 'no .past slides' ); |  | ||||||
| 		assert.strictEqual( document.querySelectorAll( '.reveal .slides section.present' ).length, 1, 'one .present slide' ); |  | ||||||
| 		assert.strictEqual( document.querySelectorAll( '.reveal .slides>section.future' ).length, horizontalSlides.length - 1, 'remaining horizontal slides are .future' ); |  | ||||||
| 
 |  | ||||||
| 		assert.strictEqual( document.querySelectorAll( '.reveal .slides section.stack' ).length, 2, 'two .stacks' ); |  | ||||||
| 
 |  | ||||||
| 		assert.ok( document.querySelectorAll( '.reveal .slides section.stack' )[0].querySelectorAll( '.future' ).length > 0, 'vertical slides are given .future' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	// ---------------------------------------------------------------
 |  | ||||||
| 	// API TESTS
 |  | ||||||
| 
 |  | ||||||
| 	QUnit.module( 'API' ); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.isReady', function( assert ) { |  | ||||||
| 		assert.strictEqual( Reveal.isReady(), true, 'returns true' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.isOverview', function( assert ) { |  | ||||||
| 		assert.strictEqual( Reveal.isOverview(), false, 'false by default' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.toggleOverview(); |  | ||||||
| 		assert.strictEqual( Reveal.isOverview(), true, 'true after toggling on' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.toggleOverview(); |  | ||||||
| 		assert.strictEqual( Reveal.isOverview(), false, 'false after toggling off' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.isPaused', function( assert ) { |  | ||||||
| 		assert.strictEqual( Reveal.isPaused(), false, 'false by default' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.togglePause(); |  | ||||||
| 		assert.strictEqual( Reveal.isPaused(), true, 'true after pausing' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.togglePause(); |  | ||||||
| 		assert.strictEqual( Reveal.isPaused(), false, 'false after resuming' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.isFirstSlide', function( assert ) { |  | ||||||
| 		Reveal.slide( 0, 0 ); |  | ||||||
| 		assert.strictEqual( Reveal.isFirstSlide(), true, 'true after Reveal.slide( 0, 0 )' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 1, 0 ); |  | ||||||
| 		assert.strictEqual( Reveal.isFirstSlide(), false, 'false after Reveal.slide( 1, 0 )' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 0, 0 ); |  | ||||||
| 		assert.strictEqual( Reveal.isFirstSlide(), true, 'true after Reveal.slide( 0, 0 )' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.isFirstSlide after vertical slide', function( assert ) { |  | ||||||
| 		Reveal.slide( 1, 1 ); |  | ||||||
| 		Reveal.slide( 0, 0 ); |  | ||||||
| 		assert.strictEqual( Reveal.isFirstSlide(), true, 'true after Reveal.slide( 1, 1 ) and then Reveal.slide( 0, 0 )' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.isLastSlide', function( assert ) { |  | ||||||
| 		Reveal.slide( 0, 0 ); |  | ||||||
| 		assert.strictEqual( Reveal.isLastSlide(), false, 'false after Reveal.slide( 0, 0 )' ); |  | ||||||
| 
 |  | ||||||
| 		var lastSlideIndex = document.querySelectorAll( '.reveal .slides>section' ).length - 1; |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( lastSlideIndex, 0 ); |  | ||||||
| 		assert.strictEqual( Reveal.isLastSlide(), true, 'true after Reveal.slide( '+ lastSlideIndex +', 0 )' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 0, 0 ); |  | ||||||
| 		assert.strictEqual( Reveal.isLastSlide(), false, 'false after Reveal.slide( 0, 0 )' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.isLastSlide after vertical slide', function( assert ) { |  | ||||||
| 		var lastSlideIndex = document.querySelectorAll( '.reveal .slides>section' ).length - 1; |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 1, 1 ); |  | ||||||
| 		Reveal.slide( lastSlideIndex ); |  | ||||||
| 		assert.strictEqual( Reveal.isLastSlide(), true, 'true after Reveal.slide( 1, 1 ) and then Reveal.slide( '+ lastSlideIndex +', 0 )' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.getTotalSlides', function( assert ) { |  | ||||||
| 		assert.strictEqual( Reveal.getTotalSlides(), 8, 'eight slides in total' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.getIndices', function( assert ) { |  | ||||||
| 		var indices = Reveal.getIndices(); |  | ||||||
| 
 |  | ||||||
| 		assert.ok( indices.hasOwnProperty( 'h' ), 'h exists' ); |  | ||||||
| 		assert.ok( indices.hasOwnProperty( 'v' ), 'v exists' ); |  | ||||||
| 		assert.ok( indices.hasOwnProperty( 'f' ), 'f exists' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 1, 0 ); |  | ||||||
| 		assert.strictEqual( Reveal.getIndices().h, 1, 'h 1' ); |  | ||||||
| 		assert.strictEqual( Reveal.getIndices().v, 0, 'v 0' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 1, 2 ); |  | ||||||
| 		assert.strictEqual( Reveal.getIndices().h, 1, 'h 1' ); |  | ||||||
| 		assert.strictEqual( Reveal.getIndices().v, 2, 'v 2' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 0, 0 ); |  | ||||||
| 		assert.strictEqual( Reveal.getIndices().h, 0, 'h 0' ); |  | ||||||
| 		assert.strictEqual( Reveal.getIndices().v, 0, 'v 0' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.getSlide', function( assert ) { |  | ||||||
| 		assert.equal( Reveal.getSlide( 0 ), document.querySelector( '.reveal .slides>section:first-child' ), 'gets correct first slide' ); |  | ||||||
| 		assert.equal( Reveal.getSlide( 1 ), document.querySelector( '.reveal .slides>section:nth-child(2)' ), 'no v index returns stack' ); |  | ||||||
| 		assert.equal( Reveal.getSlide( 1, 0 ), document.querySelector( '.reveal .slides>section:nth-child(2)>section:nth-child(1)' ), 'v index 0 returns first vertical child' ); |  | ||||||
| 		assert.equal( Reveal.getSlide( 1, 1 ), document.querySelector( '.reveal .slides>section:nth-child(2)>section:nth-child(2)' ), 'v index 1 returns second vertical child' ); |  | ||||||
| 
 |  | ||||||
| 		assert.strictEqual( Reveal.getSlide( 100 ), undefined, 'undefined when out of horizontal bounds' ); |  | ||||||
| 		assert.strictEqual( Reveal.getSlide( 1, 100 ), undefined, 'undefined when out of vertical bounds' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.getSlideBackground', function( assert ) { |  | ||||||
| 		assert.equal( Reveal.getSlideBackground( 0 ), document.querySelector( '.reveal .backgrounds>.slide-background:first-child' ), 'gets correct first background' ); |  | ||||||
| 		assert.equal( Reveal.getSlideBackground( 1 ), document.querySelector( '.reveal .backgrounds>.slide-background:nth-child(2)' ), 'no v index returns stack' ); |  | ||||||
| 		assert.equal( Reveal.getSlideBackground( 1, 0 ), document.querySelector( '.reveal .backgrounds>.slide-background:nth-child(2) .slide-background:nth-child(2)' ), 'v index 0 returns first vertical child' ); |  | ||||||
| 		assert.equal( Reveal.getSlideBackground( 1, 1 ), document.querySelector( '.reveal .backgrounds>.slide-background:nth-child(2) .slide-background:nth-child(3)' ), 'v index 1 returns second vertical child' ); |  | ||||||
| 
 |  | ||||||
| 		assert.strictEqual( Reveal.getSlideBackground( 100 ), undefined, 'undefined when out of horizontal bounds' ); |  | ||||||
| 		assert.strictEqual( Reveal.getSlideBackground( 1, 100 ), undefined, 'undefined when out of vertical bounds' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.getSlideNotes', function( assert ) { |  | ||||||
| 		Reveal.slide( 0, 0 ); |  | ||||||
| 		assert.ok( Reveal.getSlideNotes() === 'speaker notes 1', 'works with <aside class="notes">' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 1, 0 ); |  | ||||||
| 		assert.ok( Reveal.getSlideNotes() === 'speaker notes 2', 'works with <section data-notes="">' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.getPreviousSlide/getCurrentSlide', function( assert ) { |  | ||||||
| 		Reveal.slide( 0, 0 ); |  | ||||||
| 		Reveal.slide( 1, 0 ); |  | ||||||
| 
 |  | ||||||
| 		var firstSlide = document.querySelector( '.reveal .slides>section:first-child' ); |  | ||||||
| 		var secondSlide = document.querySelector( '.reveal .slides>section:nth-child(2)>section' ); |  | ||||||
| 
 |  | ||||||
| 		assert.equal( Reveal.getPreviousSlide(), firstSlide, 'previous is slide #0' ); |  | ||||||
| 		assert.equal( Reveal.getCurrentSlide(), secondSlide, 'current is slide #1' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.getProgress', function( assert ) { |  | ||||||
| 		Reveal.slide( 0, 0 ); |  | ||||||
| 		assert.strictEqual( Reveal.getProgress(), 0, 'progress is 0 on first slide' ); |  | ||||||
| 
 |  | ||||||
| 		var lastSlideIndex = document.querySelectorAll( '.reveal .slides>section' ).length - 1; |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( lastSlideIndex, 0 ); |  | ||||||
| 		assert.strictEqual( Reveal.getProgress(), 1, 'progress is 1 on last slide' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.getScale', function( assert ) { |  | ||||||
| 		assert.ok( typeof Reveal.getScale() === 'number', 'has scale' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.getConfig', function( assert ) { |  | ||||||
| 		assert.ok( typeof Reveal.getConfig() === 'object', 'has config' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.configure', function( assert ) { |  | ||||||
| 		assert.strictEqual( Reveal.getConfig().loop, false, '"loop" is false to start with' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.configure({ loop: true }); |  | ||||||
| 		assert.strictEqual( Reveal.getConfig().loop, true, '"loop" has changed to true' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.configure({ loop: false, customTestValue: 1 }); |  | ||||||
| 		assert.strictEqual( Reveal.getConfig().customTestValue, 1, 'supports custom values' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.availableRoutes', function( assert ) { |  | ||||||
| 		Reveal.slide( 0, 0 ); |  | ||||||
| 		assert.deepEqual( Reveal.availableRoutes(), { left: false, up: false, down: false, right: true }, 'correct for first slide' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 1, 0 ); |  | ||||||
| 		assert.deepEqual( Reveal.availableRoutes(), { left: true, up: false, down: true, right: true }, 'correct for vertical slide' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.next', function( assert ) { |  | ||||||
| 		Reveal.slide( 0, 0 ); |  | ||||||
| 
 |  | ||||||
| 		// Step through vertical child slides
 |  | ||||||
| 		Reveal.next(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 1, v: 0, f: undefined } ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.next(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 1, v: 1, f: undefined } ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.next(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 1, v: 2, f: undefined } ); |  | ||||||
| 
 |  | ||||||
| 		// Step through fragments
 |  | ||||||
| 		Reveal.next(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: -1 } ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.next(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 } ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.next(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 1 } ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.next(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 2 } ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.next at end', function( assert ) { |  | ||||||
| 		Reveal.slide( 3 ); |  | ||||||
| 
 |  | ||||||
| 		// We're at the end, this should have no effect
 |  | ||||||
| 		Reveal.next(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 3, v: 0, f: undefined } ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.next(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 3, v: 0, f: undefined } ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	// ---------------------------------------------------------------
 |  | ||||||
| 	// FRAGMENT TESTS
 |  | ||||||
| 
 |  | ||||||
| 	QUnit.module( 'Fragments' ); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Sliding to fragments', function( assert ) { |  | ||||||
| 		Reveal.slide( 2, 0, -1 ); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: -1 }, 'Reveal.slide( 2, 0, -1 )' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 2, 0, 0 ); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 }, 'Reveal.slide( 2, 0, 0 )' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 2, 0, 2 ); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 2 }, 'Reveal.slide( 2, 0, 2 )' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 2, 0, 1 ); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 1 }, 'Reveal.slide( 2, 0, 1 )' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'data-fragment is set on slide <section>', function( assert ) { |  | ||||||
| 		Reveal.slide( 2, 0, -1 ); |  | ||||||
| 		assert.deepEqual( Reveal.getCurrentSlide().getAttribute( 'data-fragment' ), '-1' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 2, 0, 2 ); |  | ||||||
| 		assert.deepEqual( Reveal.getCurrentSlide().getAttribute( 'data-fragment' ), '2' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 2, 0, 0 ); |  | ||||||
| 		assert.deepEqual( Reveal.getCurrentSlide().getAttribute( 'data-fragment' ), '0' ); |  | ||||||
| 
 |  | ||||||
| 		var fragmentSlide = Reveal.getCurrentSlide(); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 3, 0 ); |  | ||||||
| 		assert.deepEqual( fragmentSlide.getAttribute( 'data-fragment' ), '0', 'data-fragment persists when jumping to another slide' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Hiding all fragments', function( assert ) { |  | ||||||
| 		var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(1)' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 2, 0, 0 ); |  | ||||||
| 		assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 1, 'one fragment visible when index is 0' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 2, 0, -1 ); |  | ||||||
| 		assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 0, 'no fragments visible when index is -1' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Current fragment', function( assert ) { |  | ||||||
| 		var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(1)' ); |  | ||||||
| 		var lastFragmentIndex = [].slice.call( fragmentSlide.querySelectorAll( '.fragment' ) ).pop().getAttribute( 'data-fragment-index' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 2, 0 ); |  | ||||||
| 		assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 0, 'no current fragment at index -1' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 2, 0, 0 ); |  | ||||||
| 		assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 1, 'one current fragment at index 0' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 1, 0, 0 ); |  | ||||||
| 		assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 0, 'no current fragment when navigating to previous slide' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 3, 0, 0 ); |  | ||||||
| 		assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 0, 'no current fragment when navigating to next slide' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 2, 1, -1 ); |  | ||||||
| 		Reveal.prev(); |  | ||||||
| 		assert.strictEqual( fragmentSlide.querySelector( '.fragment.current-fragment' ).getAttribute( 'data-fragment-index' ), lastFragmentIndex, 'last fragment is current fragment when returning from future slide' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Stepping through fragments', function( assert ) { |  | ||||||
| 		Reveal.slide( 2, 0, -1 ); |  | ||||||
| 
 |  | ||||||
| 		// forwards:
 |  | ||||||
| 
 |  | ||||||
| 		Reveal.next(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 }, 'next() goes to next fragment' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.right(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 1 }, 'right() goes to next fragment' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.down(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 2 }, 'down() goes to next fragment' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.down(); // moves to f #3
 |  | ||||||
| 
 |  | ||||||
| 		// backwards:
 |  | ||||||
| 
 |  | ||||||
| 		Reveal.prev(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 2 }, 'prev() goes to prev fragment' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.left(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 1 }, 'left() goes to prev fragment' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.up(); |  | ||||||
| 		assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 }, 'up() goes to prev fragment' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Stepping past fragments', function( assert ) { |  | ||||||
| 		var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(1)' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 0, 0, 0 ); |  | ||||||
| 		assert.equal( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 0, 'no fragments visible when on previous slide' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 3, 0, 0 ); |  | ||||||
| 		assert.equal( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 3, 'all fragments visible when on future slide' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Fragment indices', function( assert ) { |  | ||||||
| 		var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(2)' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 3, 0, 0 ); |  | ||||||
| 		assert.equal( fragmentSlide.querySelectorAll( '.fragment.visible' ).length, 2, 'both fragments of same index are shown' ); |  | ||||||
| 
 |  | ||||||
| 		// This slide has three fragments, first one is index 0, second and third have index 1
 |  | ||||||
| 		Reveal.slide( 2, 2, 0 ); |  | ||||||
| 		assert.equal( Reveal.getIndices().f, 0, 'returns correct index for first fragment' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 2, 2, 1 ); |  | ||||||
| 		assert.equal( Reveal.getIndices().f, 1, 'returns correct index for two fragments with same index' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Index generation', function( assert ) { |  | ||||||
| 		var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(1)' ); |  | ||||||
| 
 |  | ||||||
| 		// These have no indices defined to start with
 |  | ||||||
| 		assert.equal( fragmentSlide.querySelectorAll( '.fragment' )[0].getAttribute( 'data-fragment-index' ), '0' ); |  | ||||||
| 		assert.equal( fragmentSlide.querySelectorAll( '.fragment' )[1].getAttribute( 'data-fragment-index' ), '1' ); |  | ||||||
| 		assert.equal( fragmentSlide.querySelectorAll( '.fragment' )[2].getAttribute( 'data-fragment-index' ), '2' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Index normalization', function( assert ) { |  | ||||||
| 		var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(3)' ); |  | ||||||
| 
 |  | ||||||
| 		// These start out as 1-4-4 and should normalize to 0-1-1
 |  | ||||||
| 		assert.equal( fragmentSlide.querySelectorAll( '.fragment' )[0].getAttribute( 'data-fragment-index' ), '0' ); |  | ||||||
| 		assert.equal( fragmentSlide.querySelectorAll( '.fragment' )[1].getAttribute( 'data-fragment-index' ), '1' ); |  | ||||||
| 		assert.equal( fragmentSlide.querySelectorAll( '.fragment' )[2].getAttribute( 'data-fragment-index' ), '1' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'fragmentshown event', function( assert ) { |  | ||||||
| 		assert.expect( 2 ); |  | ||||||
| 		var done = assert.async( 2 ); |  | ||||||
| 
 |  | ||||||
| 		var _onEvent = function( event ) { |  | ||||||
| 			assert.ok( true, 'event fired' ); |  | ||||||
| 			done(); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		Reveal.addEventListener( 'fragmentshown', _onEvent ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 2, 0 ); |  | ||||||
| 		Reveal.slide( 2, 0 ); // should do nothing
 |  | ||||||
| 		Reveal.slide( 2, 0, 0 ); // should do nothing
 |  | ||||||
| 		Reveal.next(); |  | ||||||
| 		Reveal.next(); |  | ||||||
| 		Reveal.prev(); // shouldn't fire fragmentshown
 |  | ||||||
| 
 |  | ||||||
| 		Reveal.removeEventListener( 'fragmentshown', _onEvent ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'fragmenthidden event', function( assert ) { |  | ||||||
| 		assert.expect( 2 ); |  | ||||||
| 		var done = assert.async( 2 ); |  | ||||||
| 
 |  | ||||||
| 		var _onEvent = function( event ) { |  | ||||||
| 			assert.ok( true, 'event fired' ); |  | ||||||
| 			done(); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		Reveal.addEventListener( 'fragmenthidden', _onEvent ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 2, 0, 2 ); |  | ||||||
| 		Reveal.slide( 2, 0, 2 ); // should do nothing
 |  | ||||||
| 		Reveal.prev(); |  | ||||||
| 		Reveal.prev(); |  | ||||||
| 		Reveal.next(); // shouldn't fire fragmenthidden
 |  | ||||||
| 
 |  | ||||||
| 		Reveal.removeEventListener( 'fragmenthidden', _onEvent ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	// ---------------------------------------------------------------
 |  | ||||||
| 	// AUTO-SLIDE TESTS
 |  | ||||||
| 
 |  | ||||||
| 	QUnit.module( 'Auto Sliding' ); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.isAutoSliding', function( assert ) { |  | ||||||
| 		assert.strictEqual( Reveal.isAutoSliding(), false, 'false by default' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.configure({ autoSlide: 10000 }); |  | ||||||
| 		assert.strictEqual( Reveal.isAutoSliding(), true, 'true after starting' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.configure({ autoSlide: 0 }); |  | ||||||
| 		assert.strictEqual( Reveal.isAutoSliding(), false, 'false after setting to 0' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Reveal.toggleAutoSlide', function( assert ) { |  | ||||||
| 		Reveal.configure({ autoSlide: 10000 }); |  | ||||||
| 
 |  | ||||||
| 		Reveal.toggleAutoSlide(); |  | ||||||
| 		assert.strictEqual( Reveal.isAutoSliding(), false, 'false after first toggle' ); |  | ||||||
| 		Reveal.toggleAutoSlide(); |  | ||||||
| 		assert.strictEqual( Reveal.isAutoSliding(), true, 'true after second toggle' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.configure({ autoSlide: 0 }); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'autoslidepaused', function( assert ) { |  | ||||||
| 		assert.expect( 1 ); |  | ||||||
| 		var done = assert.async(); |  | ||||||
| 
 |  | ||||||
| 		var _onEvent = function( event ) { |  | ||||||
| 			assert.ok( true, 'event fired' ); |  | ||||||
| 			done(); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		Reveal.addEventListener( 'autoslidepaused', _onEvent ); |  | ||||||
| 		Reveal.configure({ autoSlide: 10000 }); |  | ||||||
| 		Reveal.toggleAutoSlide(); |  | ||||||
| 
 |  | ||||||
| 		// cleanup
 |  | ||||||
| 		Reveal.configure({ autoSlide: 0 }); |  | ||||||
| 		Reveal.removeEventListener( 'autoslidepaused', _onEvent ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'autoslideresumed', function( assert ) { |  | ||||||
| 		assert.expect( 1 ); |  | ||||||
| 		var done = assert.async(); |  | ||||||
| 
 |  | ||||||
| 		var _onEvent = function( event ) { |  | ||||||
| 			assert.ok( true, 'event fired' ); |  | ||||||
| 			done(); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		Reveal.addEventListener( 'autoslideresumed', _onEvent ); |  | ||||||
| 		Reveal.configure({ autoSlide: 10000 }); |  | ||||||
| 		Reveal.toggleAutoSlide(); |  | ||||||
| 		Reveal.toggleAutoSlide(); |  | ||||||
| 
 |  | ||||||
| 		// cleanup
 |  | ||||||
| 		Reveal.configure({ autoSlide: 0 }); |  | ||||||
| 		Reveal.removeEventListener( 'autoslideresumed', _onEvent ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	// ---------------------------------------------------------------
 |  | ||||||
| 	// CONFIGURATION VALUES
 |  | ||||||
| 
 |  | ||||||
| 	QUnit.module( 'Configuration' ); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Controls', function( assert ) { |  | ||||||
| 		var controlsElement = document.querySelector( '.reveal>.controls' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.configure({ controls: false }); |  | ||||||
| 		assert.equal( controlsElement.style.display, 'none', 'controls are hidden' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.configure({ controls: true }); |  | ||||||
| 		assert.equal( controlsElement.style.display, 'block', 'controls are visible' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Progress', function( assert ) { |  | ||||||
| 		var progressElement = document.querySelector( '.reveal>.progress' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.configure({ progress: false }); |  | ||||||
| 		assert.equal( progressElement.style.display, 'none', 'progress are hidden' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.configure({ progress: true }); |  | ||||||
| 		assert.equal( progressElement.style.display, 'block', 'progress are visible' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'Loop', function( assert ) { |  | ||||||
| 		Reveal.configure({ loop: true }); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 0, 0 ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.left(); |  | ||||||
| 		assert.notEqual( Reveal.getIndices().h, 0, 'looped from start to end' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.right(); |  | ||||||
| 		assert.equal( Reveal.getIndices().h, 0, 'looped from end to start' ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.configure({ loop: false }); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	// ---------------------------------------------------------------
 |  | ||||||
| 	// LAZY-LOADING TESTS
 |  | ||||||
| 
 |  | ||||||
| 	QUnit.module( 'Lazy-Loading' ); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'img with data-src', function( assert ) { |  | ||||||
| 		assert.strictEqual( document.querySelectorAll( '.reveal section img[src]' ).length, 1, 'Image source has been set' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'video with data-src', function( assert ) { |  | ||||||
| 		assert.strictEqual( document.querySelectorAll( '.reveal section video[src]' ).length, 1, 'Video source has been set' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'audio with data-src', function( assert ) { |  | ||||||
| 		assert.strictEqual( document.querySelectorAll( '.reveal section audio[src]' ).length, 1, 'Audio source has been set' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'iframe with data-src', function( assert ) { |  | ||||||
| 		Reveal.slide( 0, 0 ); |  | ||||||
| 		assert.strictEqual( document.querySelectorAll( '.reveal section iframe[src]' ).length, 0, 'Iframe source is not set' ); |  | ||||||
| 		Reveal.slide( 2, 1 ); |  | ||||||
| 		assert.strictEqual( document.querySelectorAll( '.reveal section iframe[src]' ).length, 1, 'Iframe source is set' ); |  | ||||||
| 		Reveal.slide( 2, 2 ); |  | ||||||
| 		assert.strictEqual( document.querySelectorAll( '.reveal section iframe[src]' ).length, 0, 'Iframe source is not set' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'background images', function( assert ) { |  | ||||||
| 		var imageSource1 = Reveal.getSlide( 0 ).getAttribute( 'data-background-image' ); |  | ||||||
| 		var imageSource2 = Reveal.getSlide( 1, 0 ).getAttribute( 'data-background' ); |  | ||||||
| 
 |  | ||||||
| 		// check that the images are applied to the background elements
 |  | ||||||
| 		assert.ok( Reveal.getSlideBackground( 0 ).querySelector( '.slide-background-content' ).style.backgroundImage.indexOf( imageSource1 ) !== -1, 'data-background-image worked' ); |  | ||||||
| 		assert.ok( Reveal.getSlideBackground( 1, 0 ).querySelector( '.slide-background-content' ).style.backgroundImage.indexOf( imageSource2 ) !== -1, 'data-background worked' ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	// ---------------------------------------------------------------
 |  | ||||||
| 	// EVENT TESTS
 |  | ||||||
| 
 |  | ||||||
| 	QUnit.module( 'Events' ); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'slidechanged', function( assert ) { |  | ||||||
| 		assert.expect( 3 ); |  | ||||||
| 		var done = assert.async( 3 ); |  | ||||||
| 
 |  | ||||||
| 		var _onEvent = function( event ) { |  | ||||||
| 			assert.ok( true, 'event fired' ); |  | ||||||
| 			done(); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		Reveal.addEventListener( 'slidechanged', _onEvent ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.slide( 1, 0 ); // should trigger
 |  | ||||||
| 		Reveal.slide( 1, 0 ); // should do nothing
 |  | ||||||
| 		Reveal.next(); // should trigger
 |  | ||||||
| 		Reveal.slide( 3, 0 ); // should trigger
 |  | ||||||
| 		Reveal.next(); // should do nothing
 |  | ||||||
| 
 |  | ||||||
| 		Reveal.removeEventListener( 'slidechanged', _onEvent ); |  | ||||||
| 
 |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'paused', function( assert ) { |  | ||||||
| 		assert.expect( 1 ); |  | ||||||
| 		var done = assert.async(); |  | ||||||
| 
 |  | ||||||
| 		var _onEvent = function( event ) { |  | ||||||
| 			assert.ok( true, 'event fired' ); |  | ||||||
| 			done(); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		Reveal.addEventListener( 'paused', _onEvent ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.togglePause(); |  | ||||||
| 		Reveal.togglePause(); |  | ||||||
| 
 |  | ||||||
| 		Reveal.removeEventListener( 'paused', _onEvent ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	QUnit.test( 'resumed', function( assert ) { |  | ||||||
| 		assert.expect( 1 ); |  | ||||||
| 		var done = assert.async(); |  | ||||||
| 
 |  | ||||||
| 		var _onEvent = function( event ) { |  | ||||||
| 			assert.ok( true, 'event fired' ); |  | ||||||
| 			done(); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		Reveal.addEventListener( 'resumed', _onEvent ); |  | ||||||
| 
 |  | ||||||
| 		Reveal.togglePause(); |  | ||||||
| 		Reveal.togglePause(); |  | ||||||
| 
 |  | ||||||
| 		Reveal.removeEventListener( 'resumed', _onEvent ); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| } ); |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Hakim El Hattab
						Hakim El Hattab