Browse Source

Merge remote-tracking branch 'upstream/master'

Chris Lawrence 11 years ago
parent
commit
7b119763f8

+ 100 - 56
README.md

@@ -106,6 +106,9 @@ Reveal.initialize({
 	// Transition speed
 	transitionSpeed: 'default', // default/fast/slow
 
+	// Transition style for full page backgrounds
+	backgroundTransition: 'default' // default/linear
+
 });
 ```
 
@@ -123,34 +126,6 @@ Reveal.configure({ autoSlide: 5000 });
 ```
 
 
-### Presentation Size
-
-All presentations have a normal size, that is the resolution at which they are authored. The framework will automatically scale presentations uniformly based on this size to ensure that everything fits on any given display or viewport.
-
-See below for a list of configuration options related to sizing, including default values:
-
-```javascript
-Reveal.initialize({
-
-	...
-
-	// The "normal" size of the presentation, aspect ratio will be preserved
-	// when the presentation is scaled to fit different resolutions. Can be
-	// specified using percentage units.
-	width: 960,
-	height: 700,
-
-	// Factor of the display size that should remain empty around the content
-	margin: 0.1,
-
-	// Bounds for smallest/largest possible scale to apply to content
-	minScale: 0.2,
-	maxScale: 1.0
-
-});
-```
-
-
 ### Dependencies
 
 Reveal.js doesn't _rely_ on any third party scripts to work but a few optional libraries are included by default. These libraries are loaded as dependencies in the order they appear, for example:
@@ -187,9 +162,51 @@ You can add your own extensions using the same syntax. The following properties
 - **condition**: [optional] Function which must return true for the script to be loaded
 
 
+### Presentation Size
+
+All presentations have a normal size, that is the resolution at which they are authored. The framework will automatically scale presentations uniformly based on this size to ensure that everything fits on any given display or viewport.
+
+See below for a list of configuration options related to sizing, including default values:
+
+```javascript
+Reveal.initialize({
+
+	...
+
+	// The "normal" size of the presentation, aspect ratio will be preserved
+	// when the presentation is scaled to fit different resolutions. Can be
+	// specified using percentage units.
+	width: 960,
+	height: 700,
+
+	// Factor of the display size that should remain empty around the content
+	margin: 0.1,
+
+	// Bounds for smallest/largest possible scale to apply to content
+	minScale: 0.2,
+	maxScale: 1.0
+
+});
+```
+
+### Keyboard Bindings
+
+If you're unhappy with any of the default keyboard bindings you can override them using the ```keyboard``` config option:
+
+```javascript
+Reveal.configure({
+  keyboard: {
+    13: 'next', // go to the next slide when the ENTER key is pressed
+    27: function() {}, // do something custom when ESC is pressed
+    32: null // don't do anything when SPACE is pressed (i.e. disable a reveal.js default binding)
+  }
+});
+```
+
+
 ### API
 
-The ``Reveal`` class provides a minimal JavaScript API for controlling navigation and reading state:
+The ``Reveal`` class provides a JavaScript API for controlling navigation and reading state:
 
 ```javascript
 // Navigation
@@ -203,27 +220,22 @@ Reveal.next();
 Reveal.prevFragment();
 Reveal.nextFragment();
 Reveal.toggleOverview();
+Reveal.togglePause();
 
 // Retrieves the previous and current slide elements
 Reveal.getPreviousSlide();
 Reveal.getCurrentSlide();
 
 Reveal.getIndices(); // { h: 0, v: 0 } }
-```
-
-### States
-
-If you set ``data-state="somestate"`` on a slide ``<section>``, "somestate" will be applied as a class on the document element when that slide is opened. This allows you to apply broad style changes to the page based on the active slide.
-
-Furthermore you can also listen to these changes in state via JavaScript:
 
-```javascript
-Reveal.addEventListener( 'somestate', function() {
-	// TODO: Sprinkle magic
-}, false );
+// State checks
+Reveal.isFirstSlide();
+Reveal.isLastSlide();
+Reveal.isOverview();
+Reveal.isPaused();
 ```
 
-### Ready event
+### Ready Event
 
 The 'ready' event is fired when reveal.js has loaded all (synchronous) dependencies and is ready to start navigating.
 
@@ -233,7 +245,7 @@ Reveal.addEventListener( 'ready', function( event ) {
 } );
 ```
 
-### Slide change event
+### Slide Changed Event
 
 An 'slidechanged' event is fired each time the slide is changed (regardless of state). The event object holds the index values of the current slide as well as a reference to the previous and current slide HTML nodes.
 
@@ -245,27 +257,39 @@ Reveal.addEventListener( 'slidechanged', function( event ) {
 } );
 ```
 
-### Internal links
 
-It's easy to link between slides. The first example below targets the index of another slide whereas the second targets a slide with an ID attribute (```<section id="some-slide">```):
+### States
 
-```html
-<a href="#/2/2">Link</a>
-<a href="#/some-slide">Link</a>
+If you set ``data-state="somestate"`` on a slide ``<section>``, "somestate" will be applied as a class on the document element when that slide is opened. This allows you to apply broad style changes to the page based on the active slide.
+
+Furthermore you can also listen to these changes in state via JavaScript:
+
+```javascript
+Reveal.addEventListener( 'somestate', function() {
+	// TODO: Sprinkle magic
+}, false );
 ```
 
-You can also add relative navigation links, similar to the built in reveal.js controls, by appending one of the following classes on any element. Note that each element is automatically given an ```enabled``` class when it's a valid navigation route based on the current slide.
+### Slide Backgrounds
+
+Slides are contained within a limited portion of the screen by default to allow them to fit any display and scale uniformly. You can apply full page background colors or images by applying a ```data-background``` attribute to your ```<section>``` elements. Below are a few examples.
 
 ```html
-<a href="#" class="navigate-left">
-<a href="#" class="navigate-right">
-<a href="#" class="navigate-up">
-<a href="#" class="navigate-down">
-<a href="#" class="navigate-prev"> <!-- Previous vertical or horizontal slide -->
-<a href="#" class="navigate-next"> <!-- Next vertical or horizontal slide -->
+<section data-background="#ff0000">
+	<h2>All CSS color formats are supported, like rgba() or hsl().</h2>
+</section>
+<section data-background="http://example.com/image.png">
+	<h2>This slide will have a full-size background image.</h2>
+</section>
+<section data-background="http://example.com/image.png" data-background-size="100px" data-background-repeat="repeat">
+	<h2>This background image will be sized to 100px and repeated.</h2>
+</section>
 ```
 
-### Alternating transitions
+Backgrounds transition using a fade animation by default. This can be changed to a linear sliding transition by passing ```backgroundTransition: 'slide'``` to the ```Reveal.initialize()``` call. Alternatively you can set ```data-background-transition``` on any section with a background to override that specific transition.
+
+
+### Slide Transitions
 The global presentation transition is set using the ```transition``` config value. You can override the global transition for a specific slide by using the ```data-transition``` attribute:
 
 ```html
@@ -281,6 +305,27 @@ The global presentation transition is set using the ```transition``` config valu
 Note that this does not work with the page and cube transitions.
 
 
+### Internal links
+
+It's easy to link between slides. The first example below targets the index of another slide whereas the second targets a slide with an ID attribute (```<section id="some-slide">```):
+
+```html
+<a href="#/2/2">Link</a>
+<a href="#/some-slide">Link</a>
+```
+
+You can also add relative navigation links, similar to the built in reveal.js controls, by appending one of the following classes on any element. Note that each element is automatically given an ```enabled``` class when it's a valid navigation route based on the current slide.
+
+```html
+<a href="#" class="navigate-left">
+<a href="#" class="navigate-right">
+<a href="#" class="navigate-up">
+<a href="#" class="navigate-down">
+<a href="#" class="navigate-prev"> <!-- Previous vertical or horizontal slide -->
+<a href="#" class="navigate-next"> <!-- Next vertical or horizontal slide -->
+```
+
+
 ### Fragments
 Fragments are used to highlight individual elements on a slide. Every element with the class ```fragment``` will be stepped through before moving on to the next slide. Here's an example: http://lab.hakim.se/reveal-js/#/16
 
@@ -631,4 +676,3 @@ $ grunt serve
 MIT licensed
 
 Copyright (C) 2013 Hakim El Hattab, http://hakim.se
-

+ 27 - 15
css/print/pdf.css

@@ -17,8 +17,9 @@
 
 body {
 	font-size: 18pt;
-	width: auto;
-	height: auto;
+	width: 297mm;
+	height: 229mm;
+	margin: 0 auto !important;
 	border: 0;
 	padding: 0;
 	float: none !important;
@@ -88,10 +89,8 @@ ul, ol, div, p {
 
 	left: auto;
 	top: auto;
-	margin-left: auto;
-	margin-right: auto;
-	margin-top: auto;
-	padding: auto;
+	margin: 0 !important;
+	padding: 0 !important;
 
 	overflow: visible;
 	display: block;
@@ -113,18 +112,18 @@ ul, ol, div, p {
 	page-break-after: always !important;
 
 	visibility: visible !important;
-	position: static !important;
+	position: relative !important;
 	width: 100% !important;
-	height: auto !important;
-	min-height: initial !important;
+	height: 229mm !important;
+	min-height: 229mm !important;
 	display: block !important;
-	overflow: visible !important;
+	overflow: hidden !important;
 
 	left: 0 !important;
 	top: 0 !important;
-	margin-left: 0px !important;
-	margin-top: 50px !important;
-	padding: 20px 0px !important;
+	margin: 0 !important;
+	padding: 2cm 1cm 0 1cm !important;
+	box-sizing: border-box !important;
 
 	opacity: 1 !important;
 
@@ -139,9 +138,11 @@ ul, ol, div, p {
 	        transform: none !important;
 }
 .reveal section.stack {
-	margin: 0px !important;
-	padding: 0px !important;
+	margin: 0 !important;
+	padding: 0 !important;
 	page-break-after: avoid !important;
+	height: auto !important;
+	min-height: auto !important;
 }
 .reveal section .fragment {
 	opacity: 1 !important;
@@ -152,6 +153,17 @@ ul, ol, div, p {
 	    -ms-transform: none !important;
 	        transform: none !important;
 }
+.reveal section .slide-background {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	z-index: 0;
+}
+.reveal section>* {
+	position: relative;
+	z-index: 1;
+}
 .reveal img {
 	box-shadow: none;
 }

File diff suppressed because it is too large
+ 154 - 7
css/reveal.css


File diff suppressed because it is too large
+ 0 - 0
css/reveal.min.css


+ 40 - 24
index.html

@@ -36,7 +36,6 @@
 
 			<!-- Any section element inside of this container is displayed as a slide -->
 			<div class="slides">
-
 				<section>
 					<h1>Reveal.js</h1>
 					<h3>HTML Presentations Made Easy</h3>
@@ -183,29 +182,12 @@
 				</section>
 
 				<section>
-					<section data-state="alert">
-						<h2>Global State</h2>
-						<p>
-							Set <code>data-state="something"</code> on a slide and <code>"something"</code>
-							will be added as a class to the document element when the slide is open. This lets you
-							apply broader style changes, like switching the background.
-						</p>
-						<a href="#" class="image navigate-down">
-							<img width="178" height="238" src="https://s3.amazonaws.com/hakim-static/reveal-js/arrow.png" alt="Down arrow">
-						</a>
-					</section>
-					<section data-state="blackout">
-						<h2>"blackout"</h2>
-						<a href="#" class="image navigate-down">
-							<img width="178" height="238" src="https://s3.amazonaws.com/hakim-static/reveal-js/arrow.png" alt="Down arrow">
-						</a>
-					</section>
-					<section data-state="soothe">
-						<h2>"soothe"</h2>
-						<a href="#" class="image navigate-next">
-							<img width="178" height="238" src="https://s3.amazonaws.com/hakim-static/reveal-js/arrow.png" alt="Up arrow" style="-webkit-transform: rotate(-90deg);">
-						</a>
-					</section>
+					<h2>Global State</h2>
+					<p>
+						Set <code>data-state="something"</code> on a slide and <code>"something"</code>
+						will be added as a class to the document element when the slide is open. This lets you
+						apply broader style changes, like switching the background.
+					</p>
 				</section>
 
 				<section data-state="customevent">
@@ -220,6 +202,40 @@ Reveal.addEventListener( 'customevent', function() {
 					</code></pre>
 				</section>
 
+				<section>
+					<section data-background="#007777">
+						<h2>Slide Backgrounds</h2>
+						<p>
+							Set <code>data-background="#007777"</code> on a slide to change the full page background to the given color. All CSS color formats are supported.
+						</p>
+						<a href="#" class="image navigate-down">
+							<img width="178" height="238" src="https://s3.amazonaws.com/hakim-static/reveal-js/arrow.png" alt="Down arrow">
+						</a>
+					</section>
+					<section data-background="https://s3.amazonaws.com/hakim-static/reveal-js/arrow.png">
+						<h2>Image Backgrounds</h2>
+						<pre><code>&lt;section data-background="image.png"&gt;</code></pre>
+					</section>
+					<section data-background="https://s3.amazonaws.com/hakim-static/reveal-js/arrow.png" data-background-repeat="repeat" data-background-size="100px">
+						<h2>Repeated Image Backgrounds</h2>
+						<pre><code style="word-wrap: break-word;">&lt;section data-background="image.png" data-background-repeat="repeat" data-background-size="100px"&gt;</code></pre>
+					</section>
+				</section>
+
+				<section data-transition="linear" data-background="#4d7e65" data-background-transition="slide">
+					<h2>Background Transitions</h2>
+					<p>
+						Pass reveal.js the <code>backgroundTransition: 'slide'</code> config argument to make backgrounds slide rather than fade.
+					</p>
+				</section>
+
+				<section data-transition="linear" data-background="#8c4738" data-background-transition="slide">
+					<h2>Background Transition Override</h2>
+					<p>
+						You can override background transitions per slide by using <code>data-background-transition="slide"</code>.
+					</p>
+				</section>
+
 				<section>
 					<h2>Clever Quotes</h2>
 					<p>

+ 401 - 58
js/reveal.js

@@ -70,6 +70,9 @@ var Reveal = (function(){
 			// Apply a 3D roll to links on hover
 			rollingLinks: true,
 
+			// Opens links in an iframe preview overlay
+			previewLinks: false,
+
 			// Theme (see /css/theme)
 			theme: null,
 
@@ -79,6 +82,9 @@ var Reveal = (function(){
 			// Transition speed
 			transitionSpeed: 'default', // default/fast/slow
 
+			// Transition style for full page slide backgrounds
+			backgroundTransition: 'default', // default/linear
+
 			// Script dependencies to load
 			dependencies: []
 		},
@@ -120,7 +126,7 @@ var Reveal = (function(){
 								'transform' in document.body.style,
 
 		// Throttles mouse wheel navigation
-		mouseWheelTimeout = 0,
+		lastMouseWheelStep = 0,
 
 		// An interval used to automatically move on to the next slide
 		autoSlideTimeout = 0,
@@ -186,6 +192,13 @@ var Reveal = (function(){
 		dom.wrapper = document.querySelector( '.reveal' );
 		dom.slides = document.querySelector( '.reveal .slides' );
 
+		// Background element
+		if( !document.querySelector( '.reveal .backgrounds' ) ) {
+			dom.background = document.createElement( 'div' );
+			dom.background.classList.add( 'backgrounds' );
+			dom.wrapper.appendChild( dom.background );
+		}
+
 		// Progress bar
 		if( !dom.wrapper.querySelector( '.progress' ) ) {
 			var progressElement = document.createElement( 'div' );
@@ -205,11 +218,11 @@ var Reveal = (function(){
 			dom.wrapper.appendChild( controlsElement );
 		}
 
-		// Presentation background element
+		// State background element [DEPRECATED]
 		if( !dom.wrapper.querySelector( '.state-background' ) ) {
-			var backgroundElement = document.createElement( 'div' );
-			backgroundElement.classList.add( 'state-background' );
-			dom.wrapper.appendChild( backgroundElement );
+			var stateBackgroundElement = document.createElement( 'div' );
+			stateBackgroundElement.classList.add( 'state-background' );
+			dom.wrapper.appendChild( stateBackgroundElement );
 		}
 
 		// Overlay graphic which is displayed during the paused mode
@@ -237,6 +250,88 @@ var Reveal = (function(){
 
 	}
 
+	/**
+	 * Creates the slide background elements and appends them
+	 * to the background container. One element is created per
+	 * slide no matter if the given slide has visible background.
+	 */
+	function createBackgrounds() {
+
+		if( isPrintingPDF() ) {
+			document.body.classList.add( 'print-pdf' );
+		}
+
+		// Clear prior backgrounds
+		dom.background.innerHTML = '';
+		dom.background.classList.add( 'no-transition' );
+
+		// Helper method for creating a background element for the
+		// given slide
+		function _createBackground( slide, container ) {
+
+			var data = {
+				background: slide.getAttribute( 'data-background' ),
+				backgroundSize: slide.getAttribute( 'data-background-size' ),
+				backgroundColor: slide.getAttribute( 'data-background-color' ),
+				backgroundRepeat: slide.getAttribute( 'data-background-repeat' ),
+				backgroundPosition: slide.getAttribute( 'data-background-position' ),
+				backgroundTransition: slide.getAttribute( 'data-background-transition' )
+			};
+
+			var element = document.createElement( 'div' );
+			element.className = 'slide-background';
+
+			if( data.background ) {
+				// Auto-wrap image urls in url(...)
+				if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(png|jpg|jpeg|gif|bmp)$/gi.test( data.background ) ) {
+					element.style.backgroundImage = 'url('+ data.background +')';
+				}
+				else {
+					element.style.background = data.background;
+				}
+			}
+
+			// Additional and optional background properties
+			if( data.backgroundSize ) element.style.backgroundSize = data.backgroundSize;
+			if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor;
+			if( data.backgroundRepeat ) element.style.backgroundRepeat = data.backgroundRepeat;
+			if( data.backgroundPosition ) element.style.backgroundPosition = data.backgroundPosition;
+			if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition );
+
+			container.appendChild( element );
+
+			return element;
+
+		}
+
+		// Iterate over all horizontal slides
+		toArray( document.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( slideh ) {
+
+			var backgroundStack;
+
+			if( isPrintingPDF() ) {
+				backgroundStack = _createBackground( slideh, slideh );
+			}
+			else {
+				backgroundStack = _createBackground( slideh, dom.background );
+			}
+
+			// Iterate over all vertical slides
+			toArray( slideh.querySelectorAll( 'section' ) ).forEach( function( slidev ) {
+
+				if( isPrintingPDF() ) {
+					_createBackground( slidev, slidev );
+				}
+				else {
+					_createBackground( slidev, backgroundStack );
+				}
+
+			} );
+
+		} );
+
+	}
+
 	/**
 	 * Hides the address bar if we're on a mobile device.
 	 */
@@ -348,6 +443,7 @@ var Reveal = (function(){
 		dom.wrapper.classList.add( config.transition );
 
 		dom.wrapper.setAttribute( 'data-transition-speed', config.transitionSpeed );
+		dom.wrapper.setAttribute( 'data-background-transition', config.backgroundTransition );
 
 		if( dom.controls ) {
 			dom.controls.style.display = ( config.controls && dom.controls ) ? 'block' : 'none';
@@ -380,12 +476,21 @@ var Reveal = (function(){
 			document.removeEventListener( 'mousewheel', onDocumentMouseScroll, false );
 		}
 
-		// 3D links
+		// Rolling 3D links
 		if( config.rollingLinks ) {
-			enable3DLinks();
+			enableRollingLinks();
 		}
 		else {
-			disable3DLinks();
+			disableRollingLinks();
+		}
+
+		// Iframe link previews
+		if( config.previewLinks ) {
+			enablePreviewLinks();
+		}
+		else {
+			disablePreviewLinks();
+			enablePreviewLinks( '[data-preview-link]' );
 		}
 
 		// Load the theme in the config, if it's not already loaded
@@ -523,6 +628,50 @@ var Reveal = (function(){
 
 	}
 
+	/**
+	 * Retrieves the height of the given element by looking
+	 * at the position and height of its immediate children.
+	 */
+	function getAbsoluteHeight( element ) {
+
+		var height = 0;
+
+		if( element ) {
+			var absoluteChildren = 0;
+
+			toArray( element.childNodes ).forEach( function( child ) {
+
+				if( typeof child.offsetTop === 'number' && child.style ) {
+					// Count # of abs children
+					if( child.style.position === 'absolute' ) {
+						absoluteChildren += 1;
+					}
+
+					height = Math.max( height, child.offsetTop + child.offsetHeight );
+				}
+
+			} );
+
+			// If there are no absolute children, use offsetHeight
+			if( absoluteChildren === 0 ) {
+				height = element.offsetHeight;
+			}
+
+		}
+
+		return height;
+
+	}
+
+	/**
+	 * Checks if this instance is being used to print a PDF.
+	 */
+	function isPrintingPDF() {
+
+		return ( /print-pdf/gi ).test( window.location.search );
+
+	}
+
 	/**
 	 * Causes the address bar to hide on mobile devices,
 	 * more vertical space ftw.
@@ -560,7 +709,7 @@ var Reveal = (function(){
 	/**
 	 * Wrap all links in 3D goodness.
 	 */
-	function enable3DLinks() {
+	function enableRollingLinks() {
 
 		if( supports3DTransforms && !( 'msPerspective' in document.body.style ) ) {
 			var anchors = document.querySelectorAll( SLIDES_SELECTOR + ' a:not(.image)' );
@@ -585,7 +734,7 @@ var Reveal = (function(){
 	/**
 	 * Unwrap all 3D links.
 	 */
-	function disable3DLinks() {
+	function disableRollingLinks() {
 
 		var anchors = document.querySelectorAll( SLIDES_SELECTOR + ' a.roll' );
 
@@ -601,6 +750,90 @@ var Reveal = (function(){
 
 	}
 
+	/**
+	 * Bind preview frame links.
+	 */
+	function enablePreviewLinks( selector ) {
+
+		var anchors = toArray( document.querySelectorAll( selector ? selector : 'a' ) );
+
+		anchors.forEach( function( element ) {
+			if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) {
+				element.addEventListener( 'click', onPreviewLinkClicked, false );
+			}
+		} );
+
+	}
+
+	/**
+	 * Unbind preview frame links.
+	 */
+	function disablePreviewLinks() {
+
+		var anchors = toArray( document.querySelectorAll( 'a' ) );
+
+		anchors.forEach( function( element ) {
+			if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) {
+				element.removeEventListener( 'click', onPreviewLinkClicked, false );
+			}
+		} );
+
+	}
+
+	/**
+	 * Opens a preview window for the target URL.
+	 */
+	function openPreview( url ) {
+
+		closePreview();
+
+		dom.preview = document.createElement( 'div' );
+		dom.preview.classList.add( 'preview-link-overlay' );
+		dom.wrapper.appendChild( dom.preview );
+
+		dom.preview.innerHTML = [
+			'<header>',
+				'<a class="close" href="#"><span class="icon"></span></a>',
+				'<a class="external" href="'+ url +'" target="_blank"><span class="icon"></span></a>',
+			'</header>',
+			'<div class="spinner"></div>',
+			'<div class="viewport">',
+				'<iframe src="'+ url +'"></iframe>',
+			'</div>'
+		].join('');
+
+		dom.preview.querySelector( 'iframe' ).addEventListener( 'load', function( event ) {
+			dom.preview.classList.add( 'loaded' );
+		}, false );
+
+		dom.preview.querySelector( '.close' ).addEventListener( 'click', function( event ) {
+			closePreview();
+			event.preventDefault();
+		}, false );
+
+		dom.preview.querySelector( '.external' ).addEventListener( 'click', function( event ) {
+			closePreview();
+		}, false );
+
+		setTimeout( function() {
+			dom.preview.classList.add( 'visible' );
+		}, 1 );
+
+	}
+
+	/**
+	 * Closes the iframe preview window.
+	 */
+	function closePreview() {
+
+		if( dom.preview ) {
+			dom.preview.setAttribute( 'src', '' );
+			dom.preview.parentNode.removeChild( dom.preview );
+			dom.preview = null;
+		}
+
+	}
+
 	/**
 	 * Return a sorted fragments list, ordered by an increasing
 	 * "data-fragment-index" attribute.
@@ -639,7 +872,7 @@ var Reveal = (function(){
 	 */
 	function layout() {
 
-		if( dom.wrapper ) {
+		if( dom.wrapper && !isPrintingPDF() ) {
 
 			// Available space to scale within
 			var availableWidth = dom.wrapper.offsetWidth,
@@ -707,7 +940,7 @@ var Reveal = (function(){
 						slide.style.top = 0;
 					}
 					else {
-						slide.style.top = Math.max( - ( slide.offsetHeight / 2 ) - 20, -slideHeight / 2 ) + 'px';
+						slide.style.top = Math.max( - ( getAbsoluteHeight( slide ) / 2 ) - 20, -slideHeight / 2 ) + 'px';
 					}
 				}
 				else {
@@ -748,7 +981,10 @@ var Reveal = (function(){
 	function getPreviousVerticalIndex( stack ) {
 
 		if( typeof stack === 'object' && typeof stack.setAttribute === 'function' && stack.classList.contains( 'stack' ) ) {
-			return parseInt( stack.getAttribute( 'data-previous-indexv' ) || 0, 10 );
+			// Prefer manually defined start-indexv
+			var attributeName = stack.hasAttribute( 'data-start-indexv' ) ? 'data-start-indexv' : 'data-previous-indexv';
+
+			return parseInt( stack.getAttribute( attributeName ) || 0, 10 );
 		}
 
 		return 0;
@@ -1128,7 +1364,7 @@ var Reveal = (function(){
 		}
 
 		// Dispatch an event if the slide changed
-		var slideChanged = (indexh !== indexhBefore || indexv !== indexvBefore);
+		var slideChanged = ( indexh !== indexhBefore || indexv !== indexvBefore );
 		if( slideChanged ) {
 			dispatchEvent( 'slidechanged', {
 				'indexh': indexh,
@@ -1166,13 +1402,14 @@ var Reveal = (function(){
 		}
 
 		// Handle embedded content
-		if (slideChanged) {
+		if( slideChanged ) {
 			stopEmbeddedContent( previousSlide );
 			startEmbeddedContent( currentSlide );
 		}
 
 		updateControls();
 		updateProgress();
+		updateBackground();
 
 	}
 
@@ -1196,8 +1433,12 @@ var Reveal = (function(){
 		// Start auto-sliding if it's enabled
 		cueAutoSlide();
 
+		// Re-create the slide backgrounds
+		createBackgrounds();
+
 		updateControls();
 		updateProgress();
+		updateBackground();
 
 	}
 
@@ -1254,6 +1495,9 @@ var Reveal = (function(){
 				element.classList.remove( 'present' );
 				element.classList.remove( 'future' );
 
+				// http://www.w3.org/html/wg/drafts/html/master/editing.html#the-hidden-attribute
+				element.setAttribute( 'hidden', '' );
+
 				if( i < index ) {
 					// Any element previous to index is given the 'past' class
 					element.classList.add( reverse ? 'future' : 'past' );
@@ -1271,6 +1515,7 @@ var Reveal = (function(){
 
 			// Mark the current slide as present
 			slides[index].classList.add( 'present' );
+			slides[index].removeAttribute( 'hidden' );
 
 			// If this slide has a state associated with it, add it
 			// onto the current state of the deck
@@ -1402,6 +1647,37 @@ var Reveal = (function(){
 
 	}
 
+	/**
+	 * Updates the background elements to reflect the current 
+	 * slide.
+	 */
+	function updateBackground() {
+
+		// Update the classes of all backgrounds to match the 
+		// states of their slides (past/present/future)
+		toArray( dom.background.childNodes ).forEach( function( backgroundh, h ) {
+
+			// Reverse past/future classes when in RTL mode
+			var horizontalPast = config.rtl ? 'future' : 'past',
+				horizontalFuture = config.rtl ? 'past' : 'future';
+
+			backgroundh.className = 'slide-background ' + ( h < indexh ? horizontalPast : h > indexh ? horizontalFuture : 'present' );
+
+			toArray( backgroundh.childNodes ).forEach( function( backgroundv, v ) {
+
+				backgroundv.className = 'slide-background ' + ( v < indexv ? 'past' : v > indexv ? 'future' : 'present' );
+
+			} );
+
+		} );
+
+		// Allow the first background to apply without transition
+		setTimeout( function() {
+			dom.background.classList.remove( 'no-transition' );
+		}, 1 );
+
+	}
+
 	/**
 	 * Determine what available routes there are for navigation.
 	 *
@@ -1632,10 +1908,18 @@ var Reveal = (function(){
 			var fragments = sortFragments( currentSlide.querySelectorAll( '.fragment:not(.visible)' ) );
 
 			if( fragments.length ) {
-				fragments[0].classList.add( 'visible' );
+				// Find the index of the next fragment
+				var index = fragments[0].getAttribute( 'data-fragment-index' );
+
+				// Find all fragments with the same index
+				fragments = currentSlide.querySelectorAll( '.fragment[data-fragment-index="'+ index +'"]' );
 
-				// Notify subscribers of the change
-				dispatchEvent( 'fragmentshown', { fragment: fragments[0] } );
+				toArray( fragments ).forEach( function( element ) {
+					element.classList.add( 'visible' );
+
+					// Notify subscribers of the change
+					dispatchEvent( 'fragmentshown', { fragment: element } );
+				} );
 
 				updateControls();
 				return true;
@@ -1658,10 +1942,18 @@ var Reveal = (function(){
 			var fragments = sortFragments( currentSlide.querySelectorAll( '.fragment.visible' ) );
 
 			if( fragments.length ) {
-				fragments[ fragments.length - 1 ].classList.remove( 'visible' );
+				// Find the index of the previous fragment
+				var index = fragments[ fragments.length - 1 ].getAttribute( 'data-fragment-index' );
+
+				// Find all fragments with the same index
+				fragments = currentSlide.querySelectorAll( '.fragment[data-fragment-index="'+ index +'"]' );
 
-				// Notify subscribers of the change
-				dispatchEvent( 'fragmenthidden', { fragment: fragments[ fragments.length - 1 ] } );
+				toArray( fragments ).forEach( function( f ) {
+					f.classList.remove( 'visible' );
+
+					// Notify subscribers of the change
+					dispatchEvent( 'fragmenthidden', { fragment: f } );
+				} );
 
 				updateControls();
 				return true;
@@ -1761,9 +2053,9 @@ var Reveal = (function(){
 				var previousSlide = document.querySelector( HORIZONTAL_SLIDES_SELECTOR + '.past:nth-child(' + indexh + ')' );
 
 				if( previousSlide ) {
-					indexv = ( previousSlide.querySelectorAll( 'section' ).length + 1 ) || undefined;
-					indexh --;
-					slide( indexh, indexv );
+					var v = ( previousSlide.querySelectorAll( 'section' ).length - 1 ) || undefined;
+					var h = indexh - 1;
+					slide( h, v );
 				}
 			}
 		}
@@ -1808,40 +2100,75 @@ var Reveal = (function(){
 		// keyboard modifier key is present
 		if( hasFocus || (event.shiftKey && event.keyCode !== 32) || event.altKey || event.ctrlKey || event.metaKey ) return;
 
-		var triggered = true;
-
-		// while paused only allow "unpausing" keyboard events (b and .)
+		// While paused only allow "unpausing" keyboard events (b and .)
 		if( isPaused() && [66,190,191].indexOf( event.keyCode ) === -1 ) {
 			return false;
 		}
 
-		switch( event.keyCode ) {
-			// p, page up
-			case 80: case 33: navigatePrev(); break;
-			// n, page down
-			case 78: case 34: navigateNext(); break;
-			// h, left
-			case 72: case 37: navigateLeft(); break;
-			// l, right
-			case 76: case 39: navigateRight(); break;
-			// k, up
-			case 75: case 38: navigateUp(); break;
-			// j, down
-			case 74: case 40: navigateDown(); break;
-			// home
-			case 36: slide( 0 ); break;
-			// end
-			case 35: slide( Number.MAX_VALUE ); break;
-			// space
-			case 32: isOverview() ? deactivateOverview() : event.shiftKey ? navigatePrev() : navigateNext(); break;
-			// return
-			case 13: isOverview() ? deactivateOverview() : triggered = false; break;
-			// b, period, Logitech presenter tools "black screen" button
-			case 66: case 190: case 191: togglePause(); break;
-			// f
-			case 70: enterFullscreen(); break;
-			default:
-				triggered = false;
+		var triggered = false;
+
+		// 1. User defined key bindings
+		if( typeof config.keyboard === 'object' ) {
+
+			for( var key in config.keyboard ) {
+
+				// Check if this binding matches the pressed key
+				if( parseInt( key, 10 ) === event.keyCode ) {
+
+					var value = config.keyboard[ key ];
+
+					// Calback function
+					if( typeof value === 'function' ) {
+						value.apply( null, [ event ] );
+					}
+					// String shortcuts to reveal.js API
+					else if( typeof value === 'string' && typeof Reveal[ value ] === 'function' ) {
+						Reveal[ value ].call();
+					}
+
+					triggered = true;
+
+				}
+
+			}
+
+		}
+
+		// 2. System defined key bindings
+		if( triggered === false ) {
+
+			// Assume true and try to prove false
+			triggered = true;
+
+			switch( event.keyCode ) {
+				// p, page up
+				case 80: case 33: navigatePrev(); break;
+				// n, page down
+				case 78: case 34: navigateNext(); break;
+				// h, left
+				case 72: case 37: navigateLeft(); break;
+				// l, right
+				case 76: case 39: navigateRight(); break;
+				// k, up
+				case 75: case 38: navigateUp(); break;
+				// j, down
+				case 74: case 40: navigateDown(); break;
+				// home
+				case 36: slide( 0 ); break;
+				// end
+				case 35: slide( Number.MAX_VALUE ); break;
+				// space
+				case 32: isOverview() ? deactivateOverview() : event.shiftKey ? navigatePrev() : navigateNext(); break;
+				// return
+				case 13: isOverview() ? deactivateOverview() : triggered = false; break;
+				// b, period, Logitech presenter tools "black screen" button
+				case 66: case 190: case 191: togglePause(); break;
+				// f
+				case 70: enterFullscreen(); break;
+				default:
+					triggered = false;
+			}
+
 		}
 
 		// If the input resulted in a triggered action we should prevent
@@ -2010,9 +2337,10 @@ var Reveal = (function(){
 	 */
 	function onDocumentMouseScroll( event ) {
 
-		clearTimeout( mouseWheelTimeout );
+		if( Date.now() - lastMouseWheelStep > 600 ) {
+
+			lastMouseWheelStep = Date.now();
 
-		mouseWheelTimeout = setTimeout( function() {
 			var delta = event.detail || -event.wheelDelta;
 			if( delta > 0 ) {
 				navigateNext();
@@ -2020,7 +2348,8 @@ var Reveal = (function(){
 			else {
 				navigatePrev();
 			}
-		}, 100 );
+
+		}
 
 	}
 
@@ -2101,6 +2430,20 @@ var Reveal = (function(){
 
 	}
 
+	/**
+	 * Handles clicks on links that are set to preview in the
+	 * iframe overlay.
+	 */
+	function onPreviewLinkClicked( event ) {
+
+		var url = event.target.getAttribute( 'href' );
+		if( url ) {
+			openPreview( url );
+			event.preventDefault();
+		}
+
+	}
+
 
 	// --------------------------------------------------------------------//
 	// ------------------------------- API --------------------------------//
@@ -2229,4 +2572,4 @@ var Reveal = (function(){
 		}
 	};
 
-})();
+})();

File diff suppressed because it is too large
+ 1 - 1
js/reveal.min.js


+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "reveal.js",
-  "version": "2.4.0",
+  "version": "2.5.0",
   "description": "The HTML Presentation Framework",
   "homepage": "http://lab.hakim.se/reveal-js",
   "subdomain": "revealjs",

+ 1 - 0
plugin/multiplex/index.js

@@ -30,6 +30,7 @@ app.configure(function() {
 });
 
 app.get("/", function(req, res) {
+	res.writeHead(200, {'Content-Type': 'text/html'});
 	fs.createReadStream(opts.baseDir + '/index.html').pipe(res);
 });
 

+ 1 - 0
plugin/notes-server/index.js

@@ -30,6 +30,7 @@ app.configure(function() {
 });
 
 app.get("/", function(req, res) {
+	res.writeHead(200, {'Content-Type': 'text/html'});
 	fs.createReadStream(opts.baseDir + '/index.html').pipe(res);
 });
 

+ 6 - 3
plugin/notes-server/notes.html

@@ -3,6 +3,8 @@
 	<head>
 		<meta charset="utf-8">
 
+		<meta name="viewport" content="width=1150">
+
 		<title>reveal.js - Slide Notes</title>
 
 		<style>
@@ -14,6 +16,7 @@
 				font-size: 24px;
 				width: 640px;
 				margin-top: 5px;
+				clear: left;
 			}
 
 			#wrap-current-slide {
@@ -27,13 +30,13 @@
 				width: 1280px;
 				height: 1024px;
 				border: none;
-				
+
 				-webkit-transform-origin: 0 0;
 				   -moz-transform-origin: 0 0;
 				    -ms-transform-origin: 0 0;
 				     -o-transform-origin: 0 0;
 				        transform-origin: 0 0;
-				 
+
 				-webkit-transform: scale(0.5);
 				   -moz-transform: scale(0.5);
 				    -ms-transform: scale(0.5);
@@ -53,7 +56,7 @@
 				width: 1280px;
 				height: 1024px;
 				border: none;
-				
+
 				-webkit-transform-origin: 0 0;
 				   -moz-transform-origin: 0 0;
 				    -ms-transform-origin: 0 0;

+ 4 - 3
plugin/notes/notes.html

@@ -14,6 +14,7 @@
 				font-size: 24px;
 				width: 640px;
 				margin-top: 5px;
+				clear: left;
 			}
 
 			#wrap-current-slide {
@@ -212,9 +213,9 @@
 							now = new Date();
 
 						diff = now.getTime() - start.getTime();
-						hours = parseInt( diff / ( 1000 * 60 * 60 ) );
-						minutes = parseInt( ( diff / ( 1000 * 60 ) ) % 60 );
-						seconds = parseInt( ( diff / 1000 ) % 60 );
+						hours = Math.floor( diff / ( 1000 * 60 * 60 ) );
+						minutes = Math.floor( ( diff / ( 1000 * 60 ) ) % 60 );
+						seconds = Math.floor( ( diff / 1000 ) % 60 );
 
 						clockEl.innerHTML = now.toLocaleTimeString();
 						hoursEl.innerHTML = zeroPadInteger( hours );

+ 8 - 3
plugin/print-pdf/print-pdf.js

@@ -11,8 +11,13 @@
 var page = new WebPage();
 var system = require( 'system' );
 
+page.viewportSize  = {
+	width: 1024,
+	height: 768
+};
+
 page.paperSize = {
-	format: 'A4',
+	format: 'letter',
 	orientation: 'landscape',
 	margin: {
 		left: '0',
@@ -21,7 +26,6 @@ page.paperSize = {
 		bottom: '0'
 	}
 };
-page.zoomFactor = 1.5;
 
 var revealFile = system.args[1] || 'index.html?print-pdf';
 var slideFile = system.args[2] || 'slides.pdf';
@@ -36,4 +40,5 @@ page.open( revealFile, function( status ) {
 	console.log( 'Printed succesfully' );
 	page.render( slideFile );
 	phantom.exit();
-} );
+} );
+

BIN
test/assets/image1.png


BIN
test/assets/image2.png


+ 95 - 0
test/background.html

@@ -0,0 +1,95 @@
+<!doctype html>
+<html lang="en">
+
+	<head>
+		<meta charset="utf-8">
+
+		<title>reveal.js - Test</title>
+
+		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+
+		<link rel="stylesheet" href="../css/reveal.min.css">
+		<link rel="stylesheet" href="../css/theme/serif.css" id="theme">
+	</head>
+
+	<body>
+
+		<div class="reveal">
+
+			<div class="slides">
+
+				<section data-background="#00ffff">
+					<h2>data-background: #00ffff</h2>
+				</section>
+
+				<section data-background="#bb00bb">
+					<h2>data-background: #bb00bb</h2>
+				</section>
+
+				<section>
+					<section data-background="#ff0000">
+						<h2>data-background: #ff0000</h2>
+					</section>
+					<section data-background="rgba(0, 0, 0, 0.2)">
+						<h2>data-background: rgba(0, 0, 0, 0.2)</h2>
+					</section>
+					<section data-background="salmon">
+						<h2>data-background: salmon</h2>
+					</section>
+				</section>
+
+				<section data-background="rgba(0, 100, 100, 0.2)">
+					<section>
+						<h2>Background applied to stack</h2>
+					</section>
+					<section>
+						<h2>Background applied to stack</h2>
+					</section>
+					<section data-background="rgba(100, 0, 0, 0.2)">
+						<h2>Background applied to slide inside of stack</h2>
+					</section>
+				</section>
+
+				<section data-background="assets/image1.png" style="background: rgba(255,255,255,0.9)">
+					<h2>Background image</h2>
+				</section>
+
+				<section data-background="assets/image2.png" data-background-size="100px" data-background-repeat="repeat" data-background-color="#111" style="background: rgba(255,255,255,0.9)">
+					<h2>Background image</h2>
+					<pre>data-background-size="100px" data-background-repeat="repeat" data-background-color="#111"</pre>
+				</section>
+
+				<section data-background="#888888">
+					<h2>Same background twice (1/2)</h2>
+				</section>
+				<section data-background="#888888">
+					<h2>Same background twice (2/2)</h2>
+				</section>
+				
+			</div>
+
+		</div>
+
+		<script src="../lib/js/head.min.js"></script>
+		<script src="../js/reveal.min.js"></script>
+
+		<script>
+
+			// Full list of configuration options available here:
+			// https://github.com/hakimel/reveal.js#configuration
+			Reveal.initialize({
+				controls: true,
+				progress: true,
+				history: true,
+				center: true,
+				// rtl: true,
+
+				transition: 'linear',
+				// transitionSpeed: 'slow',
+				// backgroundTransition: 'linear'
+			});
+
+		</script>
+
+	</body>
+</html>

+ 58 - 0
test/media.html

@@ -0,0 +1,58 @@
+<!doctype html>
+<html lang="en">
+
+	<head>
+		<meta charset="utf-8">
+
+		<title>reveal.js - Test</title>
+
+		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+
+		<link rel="stylesheet" href="../css/reveal.min.css">
+		<link rel="stylesheet" href="../css/theme/default.css" id="theme">
+	</head>
+
+	<body>
+
+		<div class="reveal">
+
+			<div class="slides">
+
+				<section>
+					<h2>Embedded Media Test</h2>
+				</section>
+
+				<section>
+					<iframe data-autoplay width="420" height="345" src="http://www.youtube.com/embed/l3RQZ4mcr1c"></iframe>
+				</section>
+
+				<section>
+					<h2>Empty Slide</h2>
+				</section>
+
+			</div>
+
+		</div>
+
+		<script src="../lib/js/head.min.js"></script>
+		<script src="../js/reveal.min.js"></script>
+
+		<script>
+
+			// Full list of configuration options available here:
+			// https://github.com/hakimel/reveal.js#configuration
+			Reveal.initialize({
+				controls: true,
+				progress: true,
+				history: true,
+				center: true,
+
+				transition: 'linear',
+				// transitionSpeed: 'slow',
+				// backgroundTransition: 'linear'
+			});
+
+		</script>
+
+	</body>
+</html>

Some files were not shown because too many files changed in this diff