Browse Source

Merge branch 'dev' into hash-problem

Hakim El Hattab 5 years ago
parent
commit
b5fe0f8126
61 changed files with 2049 additions and 819 deletions
  1. 1 1
      .travis.yml
  2. 11 20
      Gruntfile.js
  3. 1 1
      LICENSE
  4. 68 16
      README.md
  5. 30 0
      css/reset.css
  6. 92 85
      css/reveal.css
  7. 91 87
      css/reveal.scss
  8. 1 1
      css/theme/beige.css
  9. 4 4
      css/theme/black.css
  10. 1 1
      css/theme/blood.css
  11. 1 1
      css/theme/league.css
  12. 1 1
      css/theme/moon.css
  13. 1 1
      css/theme/night.css
  14. 1 1
      css/theme/serif.css
  15. 1 1
      css/theme/simple.css
  16. 1 1
      css/theme/sky.css
  17. 1 1
      css/theme/solarized.css
  18. 1 1
      css/theme/source/black.scss
  19. 3 1
      css/theme/template/settings.scss
  20. 3 3
      css/theme/template/theme.scss
  21. 1 1
      css/theme/white.css
  22. 35 26
      demo.html
  23. 3 3
      index.html
  24. 664 285
      js/reveal.js
  25. 71 0
      lib/css/monokai.css
  26. 0 2
      lib/js/classList.js
  27. 0 6
      lib/js/head.min.js
  28. 2 0
      lib/js/promise.js
  29. 13 11
      package.json
  30. 111 21
      plugin/highlight/highlight.js
  31. 1 3
      plugin/markdown/example.html
  32. 107 73
      plugin/markdown/markdown.js
  33. 3 3
      plugin/markdown/marked.js
  34. 51 26
      plugin/math/math.js
  35. 118 76
      plugin/notes/notes.html
  36. 46 15
      plugin/notes/notes.js
  37. 1 1
      plugin/search/search.js
  38. 22 17
      plugin/zoom-js/zoom.js
  39. 1 0
      test/assets/external-script-a.js
  40. 1 0
      test/assets/external-script-b.js
  41. 1 0
      test/assets/external-script-c.js
  42. 1 0
      test/assets/external-script-d.js
  43. 2 0
      test/examples/assets/beeping.txt
  44. BIN
      test/examples/assets/beeping.wav
  45. 5 1
      test/examples/embedded-media.html
  46. 23 3
      test/examples/math.html
  47. 0 1
      test/examples/slide-backgrounds.html
  48. 0 1
      test/examples/slide-transitions.html
  49. 78 0
      test/test-dependencies-async.html
  50. 54 0
      test/test-dependencies.html
  51. 74 0
      test/test-grid-navigation.html
  52. 1 3
      test/test-markdown-element-attributes.html
  53. 5 4
      test/test-markdown-external.html
  54. 0 1
      test/test-markdown-options.html
  55. 0 1
      test/test-markdown-slide-attributes.html
  56. 0 1
      test/test-markdown.html
  57. 1 2
      test/test-pdf.html
  58. 92 0
      test/test-plugins.html
  59. 139 0
      test/test-state.html
  60. 3 4
      test/test.html
  61. 5 0
      test/test.js

+ 1 - 1
.travis.yml

@@ -1,5 +1,5 @@
 language: node_js
 language: node_js
 node_js:
 node_js:
-  - 4
+  - 11
 after_script:
 after_script:
   - npm run build -- retire
   - npm run build -- retire

+ 11 - 20
Gruntfile.js

@@ -1,7 +1,11 @@
 /* global module:false */
 /* global module:false */
 module.exports = function(grunt) {
 module.exports = function(grunt) {
-	var port = grunt.option('port') || 8000;
-	var root = grunt.option('root') || '.';
+	const sass = require('node-sass');
+
+	require('load-grunt-tasks')(grunt);
+
+	let port = grunt.option('port') || 8000;
+	let root = grunt.option('root') || '.';
 
 
 	if (!Array.isArray(root)) root = [root];
 	if (!Array.isArray(root)) root = [root];
 
 
@@ -15,7 +19,7 @@ module.exports = function(grunt) {
 				' * http://revealjs.com\n' +
 				' * http://revealjs.com\n' +
 				' * MIT licensed\n' +
 				' * MIT licensed\n' +
 				' *\n' +
 				' *\n' +
-				' * Copyright (C) 2018 Hakim El Hattab, http://hakim.se\n' +
+				' * Copyright (C) 2019 Hakim El Hattab, http://hakim.se\n' +
 				' */'
 				' */'
 		},
 		},
 
 
@@ -35,6 +39,10 @@ module.exports = function(grunt) {
 		},
 		},
 
 
 		sass: {
 		sass: {
+			options: {
+				implementation: sass,
+				sourceMap: false
+			},
 			core: {
 			core: {
 				src: 'css/reveal.scss',
 				src: 'css/reveal.scss',
 				dest: 'css/reveal.css'
 				dest: 'css/reveal.css'
@@ -145,27 +153,10 @@ module.exports = function(grunt) {
 			options: {
 			options: {
 				livereload: true
 				livereload: true
 			}
 			}
-		},
-
-		retire: {
-			js: [ 'js/reveal.js', 'lib/js/*.js', 'plugin/**/*.js' ],
-			node: [ '.' ]
 		}
 		}
 
 
 	});
 	});
 
 
-	// Dependencies
-	grunt.loadNpmTasks( 'grunt-contrib-connect' );
-	grunt.loadNpmTasks( 'grunt-contrib-cssmin' );
-	grunt.loadNpmTasks( 'grunt-contrib-jshint' );
-	grunt.loadNpmTasks( 'grunt-contrib-qunit' );
-	grunt.loadNpmTasks( 'grunt-contrib-uglify' );
-	grunt.loadNpmTasks( 'grunt-contrib-watch' );
-	grunt.loadNpmTasks( 'grunt-autoprefixer' );
-	grunt.loadNpmTasks( 'grunt-retire' );
-	grunt.loadNpmTasks( 'grunt-sass' );
-	grunt.loadNpmTasks( 'grunt-zip' );
-
 	// Default task
 	// Default task
 	grunt.registerTask( 'default', [ 'css', 'js' ] );
 	grunt.registerTask( 'default', [ 'css', 'js' ] );
 
 

+ 1 - 1
LICENSE

@@ -1,4 +1,4 @@
-Copyright (C) 2018 Hakim El Hattab, http://hakim.se, and reveal.js contributors
+Copyright (C) 2019 Hakim El Hattab, http://hakim.se, and reveal.js contributors
 
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 of this software and associated documentation files (the "Software"), to deal

File diff suppressed because it is too large
+ 68 - 16
README.md


+ 30 - 0
css/reset.css

@@ -0,0 +1,30 @@
+/* http://meyerweb.com/eric/tools/css/reset/
+   v4.0 | 20180602
+   License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+main, menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+  margin: 0;
+  padding: 0;
+  border: 0;
+  font-size: 100%;
+  font: inherit;
+  vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, main, menu, nav, section {
+  display: block;
+}

File diff suppressed because it is too large
+ 92 - 85
css/reveal.css


File diff suppressed because it is too large
+ 91 - 87
css/reveal.scss


+ 1 - 1
css/theme/beige.css

@@ -153,7 +153,7 @@ body {
   font-family: monospace;
   font-family: monospace;
   line-height: 1.2em;
   line-height: 1.2em;
   word-wrap: break-word;
   word-wrap: break-word;
-  box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
+  box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); }
 
 
 .reveal code {
 .reveal code {
   font-family: monospace;
   font-family: monospace;

+ 4 - 4
css/theme/black.css

@@ -11,8 +11,8 @@ section.has-light-background, section.has-light-background h1, section.has-light
  * GLOBAL STYLES
  * GLOBAL STYLES
  *********************************************/
  *********************************************/
 body {
 body {
-  background: #222;
-  background-color: #222; }
+  background: #191919;
+  background-color: #191919; }
 
 
 .reveal {
 .reveal {
   font-family: "Source Sans Pro", Helvetica, sans-serif;
   font-family: "Source Sans Pro", Helvetica, sans-serif;
@@ -149,7 +149,7 @@ body {
   font-family: monospace;
   font-family: monospace;
   line-height: 1.2em;
   line-height: 1.2em;
   word-wrap: break-word;
   word-wrap: break-word;
-  box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
+  box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); }
 
 
 .reveal code {
 .reveal code {
   font-family: monospace;
   font-family: monospace;
@@ -270,4 +270,4 @@ body {
  *********************************************/
  *********************************************/
 @media print {
 @media print {
   .backgrounds {
   .backgrounds {
-    background-color: #222; } }
+    background-color: #191919; } }

+ 1 - 1
css/theme/blood.css

@@ -152,7 +152,7 @@ body {
   font-family: monospace;
   font-family: monospace;
   line-height: 1.2em;
   line-height: 1.2em;
   word-wrap: break-word;
   word-wrap: break-word;
-  box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
+  box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); }
 
 
 .reveal code {
 .reveal code {
   font-family: monospace;
   font-family: monospace;

+ 1 - 1
css/theme/league.css

@@ -155,7 +155,7 @@ body {
   font-family: monospace;
   font-family: monospace;
   line-height: 1.2em;
   line-height: 1.2em;
   word-wrap: break-word;
   word-wrap: break-word;
-  box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
+  box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); }
 
 
 .reveal code {
 .reveal code {
   font-family: monospace;
   font-family: monospace;

+ 1 - 1
css/theme/moon.css

@@ -153,7 +153,7 @@ body {
   font-family: monospace;
   font-family: monospace;
   line-height: 1.2em;
   line-height: 1.2em;
   word-wrap: break-word;
   word-wrap: break-word;
-  box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
+  box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); }
 
 
 .reveal code {
 .reveal code {
   font-family: monospace;
   font-family: monospace;

+ 1 - 1
css/theme/night.css

@@ -147,7 +147,7 @@ body {
   font-family: monospace;
   font-family: monospace;
   line-height: 1.2em;
   line-height: 1.2em;
   word-wrap: break-word;
   word-wrap: break-word;
-  box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
+  box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); }
 
 
 .reveal code {
 .reveal code {
   font-family: monospace;
   font-family: monospace;

+ 1 - 1
css/theme/serif.css

@@ -149,7 +149,7 @@ body {
   font-family: monospace;
   font-family: monospace;
   line-height: 1.2em;
   line-height: 1.2em;
   word-wrap: break-word;
   word-wrap: break-word;
-  box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
+  box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); }
 
 
 .reveal code {
 .reveal code {
   font-family: monospace;
   font-family: monospace;

+ 1 - 1
css/theme/simple.css

@@ -152,7 +152,7 @@ body {
   font-family: monospace;
   font-family: monospace;
   line-height: 1.2em;
   line-height: 1.2em;
   word-wrap: break-word;
   word-wrap: break-word;
-  box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
+  box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); }
 
 
 .reveal code {
 .reveal code {
   font-family: monospace;
   font-family: monospace;

+ 1 - 1
css/theme/sky.css

@@ -156,7 +156,7 @@ body {
   font-family: monospace;
   font-family: monospace;
   line-height: 1.2em;
   line-height: 1.2em;
   word-wrap: break-word;
   word-wrap: break-word;
-  box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
+  box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); }
 
 
 .reveal code {
 .reveal code {
   font-family: monospace;
   font-family: monospace;

+ 1 - 1
css/theme/solarized.css

@@ -153,7 +153,7 @@ body {
   font-family: monospace;
   font-family: monospace;
   line-height: 1.2em;
   line-height: 1.2em;
   word-wrap: break-word;
   word-wrap: break-word;
-  box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
+  box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); }
 
 
 .reveal code {
 .reveal code {
   font-family: monospace;
   font-family: monospace;

+ 1 - 1
css/theme/source/black.scss

@@ -16,7 +16,7 @@
 
 
 
 
 // Override theme settings (see ../template/settings.scss)
 // Override theme settings (see ../template/settings.scss)
-$backgroundColor: #222;
+$backgroundColor: #191919;
 
 
 $mainColor: #fff;
 $mainColor: #fff;
 $headingColor: #fff;
 $headingColor: #fff;

+ 3 - 1
css/theme/template/settings.scss

@@ -28,6 +28,8 @@ $heading2Size: 2.11em;
 $heading3Size: 1.55em;
 $heading3Size: 1.55em;
 $heading4Size: 1.00em;
 $heading4Size: 1.00em;
 
 
+$codeFont: monospace;
+
 // Links and actions
 // Links and actions
 $linkColor: #13DAEC;
 $linkColor: #13DAEC;
 $linkColorHover: lighten( $linkColor, 20% );
 $linkColorHover: lighten( $linkColor, 20% );
@@ -40,4 +42,4 @@ $selectionColor: #fff;
 // to return a background image or gradient
 // to return a background image or gradient
 @mixin bodyBackground() {
 @mixin bodyBackground() {
 	background: $backgroundColor;
 	background: $backgroundColor;
-}
+}

+ 3 - 3
css/theme/template/theme.scss

@@ -162,16 +162,16 @@ body {
 
 
 	text-align: left;
 	text-align: left;
 	font-size: 0.55em;
 	font-size: 0.55em;
-	font-family: monospace;
+	font-family: $codeFont;
 	line-height: 1.2em;
 	line-height: 1.2em;
 
 
 	word-wrap: break-word;
 	word-wrap: break-word;
 
 
-	box-shadow: 0px 0px 6px rgba(0,0,0,0.3);
+	box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15);
 }
 }
 
 
 .reveal code {
 .reveal code {
-	font-family: monospace;
+	font-family: $codeFont;
 	text-transform: none;
 	text-transform: none;
 }
 }
 
 

+ 1 - 1
css/theme/white.css

@@ -149,7 +149,7 @@ body {
   font-family: monospace;
   font-family: monospace;
   line-height: 1.2em;
   line-height: 1.2em;
   word-wrap: break-word;
   word-wrap: break-word;
-  box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
+  box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); }
 
 
 .reveal code {
 .reveal code {
   font-family: monospace;
   font-family: monospace;

+ 35 - 26
demo.html

@@ -12,13 +12,14 @@
 		<meta name="apple-mobile-web-app-capable" content="yes">
 		<meta name="apple-mobile-web-app-capable" content="yes">
 		<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
 		<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
 
 
-		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+		<meta name="viewport" content="width=device-width, initial-scale=1.0">
 
 
+		<link rel="stylesheet" href="css/reset.css">
 		<link rel="stylesheet" href="css/reveal.css">
 		<link rel="stylesheet" href="css/reveal.css">
 		<link rel="stylesheet" href="css/theme/black.css" id="theme">
 		<link rel="stylesheet" href="css/theme/black.css" id="theme">
 
 
 		<!-- Theme used for syntax highlighting of code -->
 		<!-- Theme used for syntax highlighting of code -->
-		<link rel="stylesheet" href="lib/css/zenburn.css">
+		<link rel="stylesheet" href="lib/css/monokai.css">
 
 
 		<!-- Printing and PDF exports -->
 		<!-- Printing and PDF exports -->
 		<script>
 		<script>
@@ -93,7 +94,10 @@
 						Press <strong>ESC</strong> to enter the slide overview.
 						Press <strong>ESC</strong> to enter the slide overview.
 					</p>
 					</p>
 					<p>
 					<p>
-						Hold down alt and click on any element to zoom in on it using <a href="http://lab.hakim.se/zoom-js">zoom.js</a>. Alt + click anywhere to zoom back out.
+						Hold down the <strong>alt</strong> key (<strong>ctrl</strong> in Linux) and click on any element to zoom towards it using <a href="http://lab.hakim.se/zoom-js">zoom.js</a>. Click again to zoom back out.
+					</p>
+					<p>
+						(NOTE: Use ctrl + click in Linux.)
 					</p>
 					</p>
 				</section>
 				</section>
 
 
@@ -195,16 +199,16 @@
 					</section>
 					</section>
 					<section data-background="https://s3.amazonaws.com/hakim-static/reveal-js/image-placeholder.png">
 					<section data-background="https://s3.amazonaws.com/hakim-static/reveal-js/image-placeholder.png">
 						<h2>Image Backgrounds</h2>
 						<h2>Image Backgrounds</h2>
-						<pre><code class="hljs">&lt;section data-background="image.png"&gt;</code></pre>
+						<pre><code class="hljs html">&lt;section data-background="image.png"&gt;</code></pre>
 					</section>
 					</section>
 					<section data-background="https://s3.amazonaws.com/hakim-static/reveal-js/image-placeholder.png" data-background-repeat="repeat" data-background-size="100px">
 					<section data-background="https://s3.amazonaws.com/hakim-static/reveal-js/image-placeholder.png" data-background-repeat="repeat" data-background-size="100px">
 						<h2>Tiled Backgrounds</h2>
 						<h2>Tiled Backgrounds</h2>
-						<pre><code class="hljs" style="word-wrap: break-word;">&lt;section data-background="image.png" data-background-repeat="repeat" data-background-size="100px"&gt;</code></pre>
+						<pre><code class="hljs html" 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-background-video="https://s3.amazonaws.com/static.slid.es/site/homepage/v1/homepage-video-editor.mp4,https://s3.amazonaws.com/static.slid.es/site/homepage/v1/homepage-video-editor.webm" data-background-color="#000000">
 					<section data-background-video="https://s3.amazonaws.com/static.slid.es/site/homepage/v1/homepage-video-editor.mp4,https://s3.amazonaws.com/static.slid.es/site/homepage/v1/homepage-video-editor.webm" data-background-color="#000000">
 						<div style="background-color: rgba(0, 0, 0, 0.9); color: #fff; padding: 20px;">
 						<div style="background-color: rgba(0, 0, 0, 0.9); color: #fff; padding: 20px;">
 							<h2>Video Backgrounds</h2>
 							<h2>Video Backgrounds</h2>
-							<pre><code class="hljs" style="word-wrap: break-word;">&lt;section data-background-video="video.mp4,video.webm"&gt;</code></pre>
+							<pre><code class="hljs html" style="word-wrap: break-word;">&lt;section data-background-video="video.mp4,video.webm"&gt;</code></pre>
 						</div>
 						</div>
 					</section>
 					</section>
 					<section data-background="http://i.giphy.com/90F8aUepslB84.gif">
 					<section data-background="http://i.giphy.com/90F8aUepslB84.gif">
@@ -217,7 +221,7 @@
 					<p>
 					<p>
 						Different background transitions are available via the backgroundTransition option. This one's called "zoom".
 						Different background transitions are available via the backgroundTransition option. This one's called "zoom".
 					</p>
 					</p>
-					<pre><code class="hljs">Reveal.configure({ backgroundTransition: 'zoom' })</code></pre>
+					<pre><code class="hljs javascript">Reveal.configure({ backgroundTransition: 'zoom' })</code></pre>
 				</section>
 				</section>
 
 
 				<section data-transition="slide" data-background="#b5533c" data-background-transition="zoom">
 				<section data-transition="slide" data-background="#b5533c" data-background-transition="zoom">
@@ -225,25 +229,32 @@
 					<p>
 					<p>
 						You can override background transitions per-slide.
 						You can override background transitions per-slide.
 					</p>
 					</p>
-					<pre><code class="hljs" style="word-wrap: break-word;">&lt;section data-background-transition="zoom"&gt;</code></pre>
+					<pre><code class="hljs html" style="word-wrap: break-word;">&lt;section data-background-transition="zoom"&gt;</code></pre>
+				</section>
+
+				<section data-background-iframe="https://hakim.se" data-background-interactive>
+					<div style="position: absolute; width: 40%; right: 0; box-shadow: 0 1px 4px rgba(0,0,0,0.5), 0 5px 25px rgba(0,0,0,0.2); background-color: rgba(0, 0, 0, 0.9); color: #fff; padding: 20px; font-size: 20px; text-align: left;">
+						<h2>Iframe Backgrounds</h2>
+						<p>Since reveal.js runs on the web, you can easily embed other web content. Try interacting with the page in the background.</p>
+					</div>
 				</section>
 				</section>
 
 
 				<section>
 				<section>
 					<h2>Pretty Code</h2>
 					<h2>Pretty Code</h2>
-					<pre><code class="hljs" data-trim contenteditable>
-function linkify( selector ) {
-  if( supports3DTransforms ) {
-
-    var nodes = document.querySelectorAll( selector );
-
-    for( var i = 0, len = nodes.length; i &lt; len; i++ ) {
-      var node = nodes[i];
-
-      if( !node.className ) {
-        node.className += ' roll';
-      }
-    }
-  }
+					<pre><code class="hljs" data-trim data-line-numbers="4,8-9">
+import React, { useState } from 'react';
+
+function Example() {
+  const [count, setCount] = useState(0);
+
+  return (
+    &lt;div&gt;
+      &lt;p&gt;You clicked {count} times&lt;/p&gt;
+      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;
+        Click me
+      &lt;/button&gt;
+    &lt;/div&gt;
+  );
 }
 }
 					</code></pre>
 					</code></pre>
 					<p>Code syntax highlighting courtesy of <a href="http://softwaremaniacs.org/soft/highlight/en/description/">highlight.js</a>.</p>
 					<p>Code syntax highlighting courtesy of <a href="http://softwaremaniacs.org/soft/highlight/en/description/">highlight.js</a>.</p>
@@ -384,7 +395,6 @@ Reveal.addEventListener( 'customevent', function() {
 
 
 		</div>
 		</div>
 
 
-		<script src="lib/js/head.min.js"></script>
 		<script src="js/reveal.js"></script>
 		<script src="js/reveal.js"></script>
 
 
 		<script>
 		<script>
@@ -393,17 +403,16 @@ Reveal.addEventListener( 'customevent', function() {
 			Reveal.initialize({
 			Reveal.initialize({
 				controls: true,
 				controls: true,
 				progress: true,
 				progress: true,
-				history: true,
 				center: true,
 				center: true,
+				hash: true,
 
 
 				transition: 'slide', // none/fade/slide/convex/concave/zoom
 				transition: 'slide', // none/fade/slide/convex/concave/zoom
 
 
 				// More info https://github.com/hakimel/reveal.js#dependencies
 				// More info https://github.com/hakimel/reveal.js#dependencies
 				dependencies: [
 				dependencies: [
-					{ src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } },
 					{ src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
 					{ src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
 					{ src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
 					{ src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
-					{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
+					{ src: 'plugin/highlight/highlight.js', async: true },
 					{ src: 'plugin/search/search.js', async: true },
 					{ src: 'plugin/search/search.js', async: true },
 					{ src: 'plugin/zoom-js/zoom.js', async: true },
 					{ src: 'plugin/zoom-js/zoom.js', async: true },
 					{ src: 'plugin/notes/notes.js', async: true }
 					{ src: 'plugin/notes/notes.js', async: true }

+ 3 - 3
index.html

@@ -6,11 +6,12 @@
 
 
 		<title>reveal.js</title>
 		<title>reveal.js</title>
 
 
+		<link rel="stylesheet" href="css/reset.css">
 		<link rel="stylesheet" href="css/reveal.css">
 		<link rel="stylesheet" href="css/reveal.css">
 		<link rel="stylesheet" href="css/theme/black.css">
 		<link rel="stylesheet" href="css/theme/black.css">
 
 
 		<!-- Theme used for syntax highlighting of code -->
 		<!-- Theme used for syntax highlighting of code -->
-		<link rel="stylesheet" href="lib/css/zenburn.css">
+		<link rel="stylesheet" href="lib/css/monokai.css">
 
 
 		<!-- Printing and PDF exports -->
 		<!-- Printing and PDF exports -->
 		<script>
 		<script>
@@ -29,7 +30,6 @@
 			</div>
 			</div>
 		</div>
 		</div>
 
 
-		<script src="lib/js/head.min.js"></script>
 		<script src="js/reveal.js"></script>
 		<script src="js/reveal.js"></script>
 
 
 		<script>
 		<script>
@@ -41,7 +41,7 @@
 					{ src: 'plugin/markdown/marked.js' },
 					{ src: 'plugin/markdown/marked.js' },
 					{ src: 'plugin/markdown/markdown.js' },
 					{ src: 'plugin/markdown/markdown.js' },
 					{ src: 'plugin/notes/notes.js', async: true },
 					{ src: 'plugin/notes/notes.js', async: true },
-					{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } }
+					{ src: 'plugin/highlight/highlight.js', async: true }
 				]
 				]
 			});
 			});
 		</script>
 		</script>

File diff suppressed because it is too large
+ 664 - 285
js/reveal.js


+ 71 - 0
lib/css/monokai.css

@@ -0,0 +1,71 @@
+/*
+Monokai style - ported by Luigi Maselli - http://grigio.org
+*/
+
+.hljs {
+  display: block;
+  overflow-x: auto;
+  padding: 0.5em;
+  background: #272822;
+  color: #ddd;
+}
+
+.hljs-tag,
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-literal,
+.hljs-strong,
+.hljs-name {
+  color: #f92672;
+}
+
+.hljs-code {
+  color: #66d9ef;
+}
+
+.hljs-class .hljs-title {
+  color: white;
+}
+
+.hljs-attribute,
+.hljs-symbol,
+.hljs-regexp,
+.hljs-link {
+  color: #bf79db;
+}
+
+.hljs-string,
+.hljs-bullet,
+.hljs-subst,
+.hljs-title,
+.hljs-section,
+.hljs-emphasis,
+.hljs-type,
+.hljs-built_in,
+.hljs-builtin-name,
+.hljs-selector-attr,
+.hljs-selector-pseudo,
+.hljs-addition,
+.hljs-variable,
+.hljs-template-tag,
+.hljs-template-variable {
+  color: #a6e22e;
+}
+
+.hljs-comment,
+.hljs-quote,
+.hljs-deletion,
+.hljs-meta {
+  color: #75715e;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-literal,
+.hljs-doctag,
+.hljs-title,
+.hljs-section,
+.hljs-type,
+.hljs-selector-id {
+  font-weight: bold;
+}

File diff suppressed because it is too large
+ 0 - 2
lib/js/classList.js


File diff suppressed because it is too large
+ 0 - 6
lib/js/head.min.js


File diff suppressed because it is too large
+ 2 - 0
lib/js/promise.js


+ 13 - 11
package.json

@@ -20,24 +20,26 @@
     "url": "git://github.com/hakimel/reveal.js.git"
     "url": "git://github.com/hakimel/reveal.js.git"
   },
   },
   "engines": {
   "engines": {
-    "node": ">=4.0.0"
+    "node": ">=9.0.0"
   },
   },
   "devDependencies": {
   "devDependencies": {
     "express": "^4.16.2",
     "express": "^4.16.2",
-    "grunt": "^1.0.1",
+    "grunt": "^1.0.3",
+    "grunt-cli": "^1.3.2",
     "grunt-autoprefixer": "^3.0.4",
     "grunt-autoprefixer": "^3.0.4",
-    "grunt-cli": "^1.2.0",
-    "grunt-contrib-connect": "^1.0.2",
-    "grunt-contrib-cssmin": "^2.2.1",
-    "grunt-contrib-jshint": "^1.1.0",
-    "grunt-contrib-qunit": "^2.0.0",
+    "grunt-contrib-connect": "^2.0.0",
+    "grunt-contrib-cssmin": "^3.0.0",
+    "grunt-contrib-jshint": "^2.0.0",
+    "grunt-contrib-qunit": "^3.1.0",
     "grunt-contrib-uglify": "^3.3.0",
     "grunt-contrib-uglify": "^3.3.0",
-    "grunt-contrib-watch": "^1.0.0",
-    "grunt-sass": "^2.0.0",
-    "grunt-retire": "^1.0.7",
+    "grunt-contrib-watch": "^1.1.0",
+    "grunt-sass": "^3.0.2",
     "grunt-zip": "~0.17.1",
     "grunt-zip": "~0.17.1",
+    "load-grunt-tasks": "^4.0.0",
+    "node-sass": "4.11.0",
     "mustache": "^2.3.0",
     "mustache": "^2.3.0",
-    "socket.io": "^1.7.3"
+    "socket.io": "^2.2.0",
+    "typescript": "^3.3.3333"
   },
   },
   "license": "MIT"
   "license": "MIT"
 }
 }

File diff suppressed because it is too large
+ 111 - 21
plugin/highlight/highlight.js


+ 1 - 3
plugin/markdown/example.html

@@ -9,7 +9,7 @@
 		<link rel="stylesheet" href="../../css/reveal.css">
 		<link rel="stylesheet" href="../../css/reveal.css">
 		<link rel="stylesheet" href="../../css/theme/white.css" id="theme">
 		<link rel="stylesheet" href="../../css/theme/white.css" id="theme">
 
 
-        <link rel="stylesheet" href="../../lib/css/zenburn.css">
+        <link rel="stylesheet" href="../../lib/css/monokai.css">
 	</head>
 	</head>
 
 
 	<body>
 	<body>
@@ -109,7 +109,6 @@
             </div>
             </div>
 		</div>
 		</div>
 
 
-		<script src="../../lib/js/head.min.js"></script>
 		<script src="../../js/reveal.js"></script>
 		<script src="../../js/reveal.js"></script>
 
 
 		<script>
 		<script>
@@ -122,7 +121,6 @@
 
 
 				// Optional libraries used to extend on reveal.js
 				// Optional libraries used to extend on reveal.js
 				dependencies: [
 				dependencies: [
-					{ src: '../../lib/js/classList.js', condition: function() { return !document.body.classList; } },
 					{ src: 'marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
 					{ src: 'marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
                     { src: 'markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
                     { src: 'markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
                     { src: '../highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
                     { src: '../highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },

+ 107 - 73
plugin/markdown/markdown.js

@@ -7,13 +7,11 @@
 	if (typeof define === 'function' && define.amd) {
 	if (typeof define === 'function' && define.amd) {
 		root.marked = require( './marked' );
 		root.marked = require( './marked' );
 		root.RevealMarkdown = factory( root.marked );
 		root.RevealMarkdown = factory( root.marked );
-		root.RevealMarkdown.initialize();
 	} else if( typeof exports === 'object' ) {
 	} else if( typeof exports === 'object' ) {
 		module.exports = factory( require( './marked' ) );
 		module.exports = factory( require( './marked' ) );
 	} else {
 	} else {
 		// Browser globals (root is window)
 		// Browser globals (root is window)
 		root.RevealMarkdown = factory( root.marked );
 		root.RevealMarkdown = factory( root.marked );
-		root.RevealMarkdown.initialize();
 	}
 	}
 }( this, function( marked ) {
 }( this, function( marked ) {
 
 
@@ -199,81 +197,108 @@
 	 */
 	 */
 	function processSlides() {
 	function processSlides() {
 
 
-		var sections = document.querySelectorAll( '[data-markdown]'),
-			section;
+		return new Promise( function( resolve ) {
 
 
-		for( var i = 0, len = sections.length; i < len; i++ ) {
+			var externalPromises = [];
 
 
-			section = sections[i];
+			[].slice.call( document.querySelectorAll( '[data-markdown]') ).forEach( function( section, i ) {
 
 
-			if( section.getAttribute( 'data-markdown' ).length ) {
+				if( section.getAttribute( 'data-markdown' ).length ) {
 
 
-				var xhr = new XMLHttpRequest(),
-					url = section.getAttribute( 'data-markdown' );
-
-				datacharset = section.getAttribute( 'data-charset' );
-
-				// see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
-				if( datacharset != null && datacharset != '' ) {
-					xhr.overrideMimeType( 'text/html; charset=' + datacharset );
-				}
-
-				xhr.onreadystatechange = function() {
-					if( xhr.readyState === 4 ) {
-						// file protocol yields status code 0 (useful for local debug, mobile applications etc.)
-						if ( ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status === 0 ) {
+					externalPromises.push( loadExternalMarkdown( section ).then(
 
 
+						// Finished loading external file
+						function( xhr, url ) {
 							section.outerHTML = slidify( xhr.responseText, {
 							section.outerHTML = slidify( xhr.responseText, {
 								separator: section.getAttribute( 'data-separator' ),
 								separator: section.getAttribute( 'data-separator' ),
 								verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
 								verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
 								notesSeparator: section.getAttribute( 'data-separator-notes' ),
 								notesSeparator: section.getAttribute( 'data-separator-notes' ),
 								attributes: getForwardedAttributes( section )
 								attributes: getForwardedAttributes( section )
 							});
 							});
+						},
 
 
-						}
-						else {
-
+						// Failed to load markdown
+						function( xhr, url ) {
 							section.outerHTML = '<section data-state="alert">' +
 							section.outerHTML = '<section data-state="alert">' +
 								'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +
 								'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +
 								'Check your browser\'s JavaScript console for more details.' +
 								'Check your browser\'s JavaScript console for more details.' +
 								'<p>Remember that you need to serve the presentation HTML from a HTTP server.</p>' +
 								'<p>Remember that you need to serve the presentation HTML from a HTTP server.</p>' +
 								'</section>';
 								'</section>';
-
 						}
 						}
-					}
-				};
 
 
-				xhr.open( 'GET', url, false );
+					) );
+
+				}
+				else if( section.getAttribute( 'data-separator' ) || section.getAttribute( 'data-separator-vertical' ) || section.getAttribute( 'data-separator-notes' ) ) {
+
+					section.outerHTML = slidify( getMarkdownFromSlide( section ), {
+						separator: section.getAttribute( 'data-separator' ),
+						verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
+						notesSeparator: section.getAttribute( 'data-separator-notes' ),
+						attributes: getForwardedAttributes( section )
+					});
 
 
-				try {
-					xhr.send();
 				}
 				}
-				catch ( e ) {
-					alert( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e );
+				else {
+					section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) );
 				}
 				}
 
 
+			});
+
+			Promise.all( externalPromises ).then( resolve );
+
+		} );
+
+	}
+
+	function loadExternalMarkdown( section ) {
+
+		return new Promise( function( resolve, reject ) {
+
+			var xhr = new XMLHttpRequest(),
+				url = section.getAttribute( 'data-markdown' );
+
+			datacharset = section.getAttribute( 'data-charset' );
+
+			// see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
+			if( datacharset != null && datacharset != '' ) {
+				xhr.overrideMimeType( 'text/html; charset=' + datacharset );
 			}
 			}
-			else if( section.getAttribute( 'data-separator' ) || section.getAttribute( 'data-separator-vertical' ) || section.getAttribute( 'data-separator-notes' ) ) {
 
 
-				section.outerHTML = slidify( getMarkdownFromSlide( section ), {
-					separator: section.getAttribute( 'data-separator' ),
-					verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
-					notesSeparator: section.getAttribute( 'data-separator-notes' ),
-					attributes: getForwardedAttributes( section )
-				});
+			xhr.onreadystatechange = function( section, xhr ) {
+				if( xhr.readyState === 4 ) {
+					// file protocol yields status code 0 (useful for local debug, mobile applications etc.)
+					if ( ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status === 0 ) {
+
+						resolve( xhr, url );
 
 
+					}
+					else {
+
+						reject( xhr, url );
+
+					}
+				}
+			}.bind( this, section, xhr );
+
+			xhr.open( 'GET', url, true );
+
+			try {
+				xhr.send();
 			}
 			}
-			else {
-				section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) );
+			catch ( e ) {
+				alert( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e );
+				resolve( xhr, url );
 			}
 			}
-		}
+
+		} );
 
 
 	}
 	}
 
 
 	/**
 	/**
 	 * Check if a node value has the attributes pattern.
 	 * Check if a node value has the attributes pattern.
 	 * If yes, extract it and add that value as one or several attributes
 	 * If yes, extract it and add that value as one or several attributes
-	 * the the terget element.
+	 * to the target element.
 	 *
 	 *
 	 * You need Cache Killer on Chrome to see the effect on any FOM transformation
 	 * You need Cache Killer on Chrome to see the effect on any FOM transformation
 	 * directly on refresh (F5)
 	 * directly on refresh (F5)
@@ -342,44 +367,47 @@
 	 */
 	 */
 	function convertSlides() {
 	function convertSlides() {
 
 
-		var sections = document.querySelectorAll( '[data-markdown]');
-
-		for( var i = 0, len = sections.length; i < len; i++ ) {
+		var sections = document.querySelectorAll( '[data-markdown]:not([data-markdown-parsed])');
 
 
-			var section = sections[i];
+		[].slice.call( sections ).forEach( function( section ) {
 
 
-			// Only parse the same slide once
-			if( !section.getAttribute( 'data-markdown-parsed' ) ) {
+			section.setAttribute( 'data-markdown-parsed', true )
 
 
-				section.setAttribute( 'data-markdown-parsed', true )
+			var notes = section.querySelector( 'aside.notes' );
+			var markdown = getMarkdownFromSlide( section );
 
 
-				var notes = section.querySelector( 'aside.notes' );
-				var markdown = getMarkdownFromSlide( section );
-
-				section.innerHTML = marked( markdown );
-				addAttributes( 	section, section, null, section.getAttribute( 'data-element-attributes' ) ||
-								section.parentNode.getAttribute( 'data-element-attributes' ) ||
-								DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR,
-								section.getAttribute( 'data-attributes' ) ||
-								section.parentNode.getAttribute( 'data-attributes' ) ||
-								DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR);
-
-				// If there were notes, we need to re-add them after
-				// having overwritten the section's HTML
-				if( notes ) {
-					section.appendChild( notes );
-				}
+			section.innerHTML = marked( markdown );
+			addAttributes( 	section, section, null, section.getAttribute( 'data-element-attributes' ) ||
+							section.parentNode.getAttribute( 'data-element-attributes' ) ||
+							DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR,
+							section.getAttribute( 'data-attributes' ) ||
+							section.parentNode.getAttribute( 'data-attributes' ) ||
+							DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR);
 
 
+			// If there were notes, we need to re-add them after
+			// having overwritten the section's HTML
+			if( notes ) {
+				section.appendChild( notes );
 			}
 			}
 
 
-		}
+		} );
+
+		return Promise.resolve();
 
 
 	}
 	}
 
 
 	// API
 	// API
-	return {
+	var RevealMarkdown = {
+
+		/**
+		 * Starts processing and converting Markdown within the
+		 * current reveal.js deck.
+		 *
+		 * @param {function} callback function to invoke once
+		 * we've finished loading and parsing Markdown
+		 */
+		init: function( callback ) {
 
 
-		initialize: function() {
 			if( typeof marked === 'undefined' ) {
 			if( typeof marked === 'undefined' ) {
 				throw 'The reveal.js Markdown plugin requires marked to be loaded';
 				throw 'The reveal.js Markdown plugin requires marked to be loaded';
 			}
 			}
@@ -392,14 +420,14 @@
 				});
 				});
 			}
 			}
 
 
+			// marked can be configured via reveal.js config options
 			var options = Reveal.getConfig().markdown;
 			var options = Reveal.getConfig().markdown;
-
-			if ( options ) {
+			if( options ) {
 				marked.setOptions( options );
 				marked.setOptions( options );
 			}
 			}
 
 
-			processSlides();
-			convertSlides();
+			return processSlides().then( convertSlides );
+
 		},
 		},
 
 
 		// TODO: Do these belong in the API?
 		// TODO: Do these belong in the API?
@@ -409,4 +437,10 @@
 
 
 	};
 	};
 
 
+	// Register our plugin so that reveal.js will call our
+	// plugin 'init' method as part of the initialization
+	Reveal.registerPlugin( 'markdown', RevealMarkdown );
+
+	return RevealMarkdown;
+
 }));
 }));

File diff suppressed because it is too large
+ 3 - 3
plugin/markdown/marked.js


+ 51 - 26
plugin/math/math.js

@@ -7,33 +7,28 @@
 var RevealMath = window.RevealMath || (function(){
 var RevealMath = window.RevealMath || (function(){
 
 
 	var options = Reveal.getConfig().math || {};
 	var options = Reveal.getConfig().math || {};
-	options.mathjax = options.mathjax || 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js';
-	options.config = options.config || 'TeX-AMS_HTML-full';
-	options.tex2jax = options.tex2jax || {
-				inlineMath: [['$','$'],['\\(','\\)']] ,
-				skipTags: ['script','noscript','style','textarea','pre'] };
-
-	loadScript( options.mathjax + '?config=' + options.config, function() {
-
-		MathJax.Hub.Config({
-			messageStyle: 'none',
-			tex2jax: options.tex2jax,
-			skipStartupTypeset: true
-		});
-
-		// Typeset followed by an immediate reveal.js layout since
-		// the typesetting process could affect slide height
-		MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub ] );
-		MathJax.Hub.Queue( Reveal.layout );
-
-		// Reprocess equations in slides when they turn visible
-		Reveal.addEventListener( 'slidechanged', function( event ) {
-
-			MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, event.currentSlide ] );
-
-		} );
+	var mathjax = options.mathjax || 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js';
+	var config = options.config || 'TeX-AMS_HTML-full';
+	var url = mathjax + '?config=' + config;
+
+	var defaultOptions = {
+		messageStyle: 'none',
+		tex2jax: {
+			inlineMath: [ [ '$', '$' ], [ '\\(', '\\)' ] ],
+			skipTags: [ 'script', 'noscript', 'style', 'textarea', 'pre' ]
+		},
+		skipStartupTypeset: true
+	};
+
+	function defaults( options, defaultOptions ) {
+
+		for ( var i in defaultOptions ) {
+			if ( !options.hasOwnProperty( i ) ) {
+				options[i] = defaultOptions[i];
+			}
+		}
 
 
-	} );
+	}
 
 
 	function loadScript( url, callback ) {
 	function loadScript( url, callback ) {
 
 
@@ -64,4 +59,34 @@ var RevealMath = window.RevealMath || (function(){
 
 
 	}
 	}
 
 
+	return {
+		init: function() {
+
+			defaults( options, defaultOptions );
+			defaults( options.tex2jax, defaultOptions.tex2jax );
+			options.mathjax = options.config = null;
+
+			loadScript( url, function() {
+
+				MathJax.Hub.Config( options );
+
+				// Typeset followed by an immediate reveal.js layout since
+				// the typesetting process could affect slide height
+				MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub ] );
+				MathJax.Hub.Queue( Reveal.layout );
+
+				// Reprocess equations in slides when they turn visible
+				Reveal.addEventListener( 'slidechanged', function( event ) {
+
+					MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, event.currentSlide ] );
+
+				} );
+
+			} );
+
+		}
+	}
+
 })();
 })();
+
+Reveal.registerPlugin( 'math', RevealMath );

+ 118 - 76
plugin/notes/notes.html

@@ -347,6 +347,8 @@
 					upcomingSlide,
 					upcomingSlide,
 					layoutLabel,
 					layoutLabel,
 					layoutDropdown,
 					layoutDropdown,
+					pendingCalls = {},
+					lastRevealApiCallId = 0,
 					connected = false;
 					connected = false;
 
 
 				var SPEAKER_LAYOUTS = {
 				var SPEAKER_LAYOUTS = {
@@ -382,6 +384,10 @@
 						else if( data.type === 'state' ) {
 						else if( data.type === 'state' ) {
 							handleStateMessage( data );
 							handleStateMessage( data );
 						}
 						}
+						else if( data.type === 'return' ) {
+							pendingCalls[data.callId](data.result);
+							delete pendingCalls[data.callId];
+						}
 					}
 					}
 					// Messages sent by the reveal.js inside of the current slide preview
 					// Messages sent by the reveal.js inside of the current slide preview
 					else if( data && data.namespace === 'reveal' ) {
 					else if( data && data.namespace === 'reveal' ) {
@@ -398,6 +404,23 @@
 
 
 				} );
 				} );
 
 
+				/**
+				 * Asynchronously calls the Reveal.js API of the main frame.
+				 */
+				function callRevealApi( methodName, methodArguments, callback ) {
+
+					var callId = ++lastRevealApiCallId;
+					pendingCalls[callId] = callback;
+					window.opener.postMessage( JSON.stringify( {
+						namespace: 'reveal-notes',
+						type: 'call',
+						callId: callId,
+						methodName: methodName,
+						arguments: methodArguments
+					} ), '*' );
+
+				}
+
 				/**
 				/**
 				 * Called when the main window is trying to establish a
 				 * Called when the main window is trying to establish a
 				 * connection.
 				 * connection.
@@ -512,28 +535,34 @@
 
 
 				}
 				}
 
 
-				function getTimings() {
+				function getTimings( callback ) {
 
 
-					var slides = Reveal.getSlides();
-					var defaultTiming = Reveal.getConfig().defaultTiming;
-					if (defaultTiming == null) {
-						return null;
-					}
-					var timings = [];
-					for ( var i in slides ) {
-						var slide = slides[i];
-						var timing = defaultTiming;
-						if( slide.hasAttribute( 'data-timing' )) {
-							var t = slide.getAttribute( 'data-timing' );
-							timing = parseInt(t);
-							if( isNaN(timing) ) {
-								console.warn("Could not parse timing '" + t + "' of slide " + i + "; using default of " + defaultTiming);
-								timing = defaultTiming;
+					callRevealApi( 'getSlidesAttributes', [], function ( slideAttributes ) {
+						callRevealApi( 'getConfig', [], function ( config ) {
+							var defaultTiming = config.defaultTiming;
+							if (defaultTiming == null) {
+								callback(null);
+								return;
+							}
+
+							var timings = [];
+							for ( var i in slideAttributes ) {
+								var slide = slideAttributes[ i ];
+								var timing = defaultTiming;
+								if( slide.hasOwnProperty( 'data-timing' )) {
+									var t = slide[ 'data-timing' ];
+									timing = parseInt(t);
+									if( isNaN(timing) ) {
+										console.warn("Could not parse timing '" + t + "' of slide " + i + "; using default of " + defaultTiming);
+										timing = defaultTiming;
+									}
+								}
+								timings.push(timing);
 							}
 							}
-						}
-						timings.push(timing);
-					}
-					return timings;
+
+							callback( timings );
+						} );
+					} );
 
 
 				}
 				}
 
 
@@ -541,15 +570,15 @@
 				 * Return the number of seconds allocated for presenting
 				 * Return the number of seconds allocated for presenting
 				 * all slides up to and including this one.
 				 * all slides up to and including this one.
 				 */
 				 */
-				function getTimeAllocated(timings) {
+				function getTimeAllocated( timings, callback ) {
 
 
-					var slides = Reveal.getSlides();
-					var allocated = 0;
-					var currentSlide = Reveal.getSlidePastCount();
-					for (var i in slides.slice(0, currentSlide + 1)) {
-						allocated += timings[i];
-					}
-					return allocated;
+					callRevealApi( 'getSlidePastCount', [], function ( currentSlide ) {
+						var allocated = 0;
+						for (var i in timings.slice(0, currentSlide + 1)) {
+							allocated += timings[i];
+						}
+						callback( allocated );
+					} );
 
 
 				}
 				}
 
 
@@ -571,12 +600,51 @@
 					pacingMinutesEl = pacingEl.querySelector( '.minutes-value' ),
 					pacingMinutesEl = pacingEl.querySelector( '.minutes-value' ),
 					pacingSecondsEl = pacingEl.querySelector( '.seconds-value' );
 					pacingSecondsEl = pacingEl.querySelector( '.seconds-value' );
 
 
-					var timings = getTimings();
-					if (timings !== null) {
-						pacingTitleEl.style.removeProperty('display');
-						pacingEl.style.removeProperty('display');
+					var timings = null;
+					getTimings( function ( _timings ) {
+
+						timings = _timings;
+						if (_timings !== null) {
+							pacingTitleEl.style.removeProperty('display');
+							pacingEl.style.removeProperty('display');
+						}
+
+						// Update once directly
+						_updateTimer();
+
+						// Then update every second
+						setInterval( _updateTimer, 1000 );
+
+					} );
+
+
+					function _resetTimer() {
+
+						if (timings == null) {
+							start = new Date();
+							_updateTimer();
+						}
+						else {
+							// Reset timer to beginning of current slide
+							getTimeAllocated( timings, function ( slideEndTimingSeconds ) {
+								var slideEndTiming = slideEndTimingSeconds * 1000;
+								callRevealApi( 'getSlidePastCount', [], function ( currentSlide ) {
+									var currentSlideTiming = timings[currentSlide] * 1000;
+									var previousSlidesTiming = slideEndTiming - currentSlideTiming;
+									var now = new Date();
+									start = new Date(now.getTime() - previousSlidesTiming);
+									_updateTimer();
+								} );
+							} );
+						}
+
 					}
 					}
 
 
+					timeEl.addEventListener( 'click', function() {
+						_resetTimer();
+						return false;
+					} );
+
 					function _displayTime( hrEl, minEl, secEl, time) {
 					function _displayTime( hrEl, minEl, secEl, time) {
 
 
 						var sign = Math.sign(time) == -1 ? "-" : "";
 						var sign = Math.sign(time) == -1 ? "-" : "";
@@ -618,52 +686,26 @@
 
 
 					function _updatePacing(diff) {
 					function _updatePacing(diff) {
 
 
-						var slideEndTiming = getTimeAllocated(timings) * 1000;
-						var currentSlide = Reveal.getSlidePastCount();
-						var currentSlideTiming = timings[currentSlide] * 1000;
-						var timeLeftCurrentSlide = slideEndTiming - diff;
-						if (timeLeftCurrentSlide < 0) {
-							pacingEl.className = 'pacing behind';
-						}
-						else if (timeLeftCurrentSlide < currentSlideTiming) {
-							pacingEl.className = 'pacing on-track';
-						}
-						else {
-							pacingEl.className = 'pacing ahead';
-						}
-						_displayTime( pacingHoursEl, pacingMinutesEl, pacingSecondsEl, timeLeftCurrentSlide );
-
-					}
-
-					// Update once directly
-					_updateTimer();
-
-					// Then update every second
-					setInterval( _updateTimer, 1000 );
-
-					function _resetTimer() {
-
-						if (timings == null) {
-							start = new Date();
-						}
-						else {
-							// Reset timer to beginning of current slide
-							var slideEndTiming = getTimeAllocated(timings) * 1000;
-							var currentSlide = Reveal.getSlidePastCount();
-							var currentSlideTiming = timings[currentSlide] * 1000;
-							var previousSlidesTiming = slideEndTiming - currentSlideTiming;
-							var now = new Date();
-							start = new Date(now.getTime() - previousSlidesTiming);
-						}
-						_updateTimer();
-
+						getTimeAllocated( timings, function ( slideEndTimingSeconds ) {
+							var slideEndTiming = slideEndTimingSeconds * 1000;
+
+							callRevealApi( 'getSlidePastCount', [], function ( currentSlide ) {
+								var currentSlideTiming = timings[currentSlide] * 1000;
+								var timeLeftCurrentSlide = slideEndTiming - diff;
+								if (timeLeftCurrentSlide < 0) {
+									pacingEl.className = 'pacing behind';
+								}
+								else if (timeLeftCurrentSlide < currentSlideTiming) {
+									pacingEl.className = 'pacing on-track';
+								}
+								else {
+									pacingEl.className = 'pacing ahead';
+								}
+								_displayTime( pacingHoursEl, pacingMinutesEl, pacingSecondsEl, timeLeftCurrentSlide );
+							} );
+						} );
 					}
 					}
 
 
-					timeEl.addEventListener( 'click', function() {
-						_resetTimer();
-						return false;
-					} );
-
 				}
 				}
 
 
 				/**
 				/**

+ 46 - 15
plugin/notes/notes.js

@@ -11,24 +11,28 @@
  */
  */
 var RevealNotes = (function() {
 var RevealNotes = (function() {
 
 
+    var notesPopup = null;
+
 	function openNotes( notesFilePath ) {
 	function openNotes( notesFilePath ) {
 
 
+        if (notesPopup && !notesPopup.closed) {
+            notesPopup.focus();
+            return;
+        }
+
 		if( !notesFilePath ) {
 		if( !notesFilePath ) {
 			var jsFileLocation = document.querySelector('script[src$="notes.js"]').src;  // this js file path
 			var jsFileLocation = document.querySelector('script[src$="notes.js"]').src;  // this js file path
 			jsFileLocation = jsFileLocation.replace(/notes\.js(\?.*)?$/, '');   // the js folder path
 			jsFileLocation = jsFileLocation.replace(/notes\.js(\?.*)?$/, '');   // the js folder path
 			notesFilePath = jsFileLocation + 'notes.html';
 			notesFilePath = jsFileLocation + 'notes.html';
 		}
 		}
 
 
-		var notesPopup = window.open( notesFilePath, 'reveal.js - Notes', 'width=1100,height=700' );
+		notesPopup = window.open( notesFilePath, 'reveal.js - Notes', 'width=1100,height=700' );
 
 
 		if( !notesPopup ) {
 		if( !notesPopup ) {
 			alert( 'Speaker view popup failed to open. Please make sure popups are allowed and reopen the speaker view.' );
 			alert( 'Speaker view popup failed to open. Please make sure popups are allowed and reopen the speaker view.' );
 			return;
 			return;
 		}
 		}
 
 
-		// Allow popup window access to Reveal API
-		notesPopup.Reveal = window.Reveal;
-
 		/**
 		/**
 		 * Connect to the notes window through a postmessage handshake.
 		 * Connect to the notes window through a postmessage handshake.
 		 * Using postmessage enables us to work in situations where the
 		 * Using postmessage enables us to work in situations where the
@@ -52,9 +56,28 @@ var RevealNotes = (function() {
 					clearInterval( connectInterval );
 					clearInterval( connectInterval );
 					onConnected();
 					onConnected();
 				}
 				}
+				if( data && data.namespace === 'reveal-notes' && data.type === 'call' ) {
+					callRevealApi( data.methodName, data.arguments, data.callId );
+				}
 			} );
 			} );
 		}
 		}
 
 
+		/**
+		 * Calls the specified Reveal.js method with the provided argument
+		 * and then pushes the result to the notes frame.
+		 */
+		function callRevealApi( methodName, methodArguments, callId ) {
+
+			var result = Reveal[methodName].apply( Reveal, methodArguments );
+			notesPopup.postMessage( JSON.stringify( {
+				namespace: 'reveal-notes',
+				type: 'return',
+				result: result,
+				callId: callId
+			} ), '*' );
+
+		}
+
 		/**
 		/**
 		 * Posts the current slide data to the notes window
 		 * Posts the current slide data to the notes window
 		 */
 		 */
@@ -128,20 +151,28 @@ var RevealNotes = (function() {
 
 
 	}
 	}
 
 
-	if( !/receiver/i.test( window.location.search ) ) {
+	return {
+		init: function() {
 
 
-		// If the there's a 'notes' query set, open directly
-		if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) {
-			openNotes();
-		}
+			if( !/receiver/i.test( window.location.search ) ) {
 
 
-		// Open the notes when the 's' key is hit
-		Reveal.addKeyBinding({keyCode: 83, key: 'S', description: 'Speaker notes view'}, function() {
-			openNotes();
-		} );
+				// If the there's a 'notes' query set, open directly
+				if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) {
+					openNotes();
+				}
 
 
-	}
+				// Open the notes when the 's' key is hit
+				Reveal.addKeyBinding({keyCode: 83, key: 'S', description: 'Speaker notes view'}, function() {
+					openNotes();
+				} );
 
 
-	return { open: openNotes };
+			}
+
+		},
+
+		open: openNotes
+	};
 
 
 })();
 })();
+
+Reveal.registerPlugin( 'notes', RevealNotes );

+ 1 - 1
plugin/search/search.js

@@ -200,7 +200,7 @@ function Hilitor(id, tag)
 			toggleSearch();
 			toggleSearch();
 		}
 		}
 	}, false );
 	}, false );
-	if( window.Reveal ) Reveal.registerKeyboardShortcut( 'Ctrl-Shift-F', 'Search' );
+	if( window.Reveal ) Reveal.registerKeyboardShortcut( 'CTRL + Shift + F', 'Search' );
 	closeSearch();
 	closeSearch();
 	return { open: openSearch };
 	return { open: openSearch };
 })();
 })();

+ 22 - 17
plugin/zoom-js/zoom.js

@@ -1,29 +1,34 @@
 // Custom reveal.js integration
 // Custom reveal.js integration
-(function(){
-	var revealElement = document.querySelector( '.reveal' );
-	if( revealElement ) {
+var RevealZoom = (function(){
 
 
-		revealElement.addEventListener( 'mousedown', function( event ) {
-			var defaultModifier = /Linux/.test( window.navigator.platform ) ? 'ctrl' : 'alt';
+	return {
+		init: function() {
 
 
-			var modifier = ( Reveal.getConfig().zoomKey ? Reveal.getConfig().zoomKey : defaultModifier ) + 'Key';
-			var zoomLevel = ( Reveal.getConfig().zoomLevel ? Reveal.getConfig().zoomLevel : 2 );
+			Reveal.getRevealElement().addEventListener( 'mousedown', function( event ) {
+				var defaultModifier = /Linux/.test( window.navigator.platform ) ? 'ctrl' : 'alt';
 
 
-			if( event[ modifier ] && !Reveal.isOverview() ) {
-				event.preventDefault();
+				var modifier = ( Reveal.getConfig().zoomKey ? Reveal.getConfig().zoomKey : defaultModifier ) + 'Key';
+				var zoomLevel = ( Reveal.getConfig().zoomLevel ? Reveal.getConfig().zoomLevel : 2 );
 
 
-				zoom.to({
-					x: event.clientX,
-					y: event.clientY,
-					scale: zoomLevel,
-					pan: false
-				});
-			}
-		} );
+				if( event[ modifier ] && !Reveal.isOverview() ) {
+					event.preventDefault();
+
+					zoom.to({
+						x: event.clientX,
+						y: event.clientY,
+						scale: zoomLevel,
+						pan: false
+					});
+				}
+			} );
 
 
+		}
 	}
 	}
+
 })();
 })();
 
 
+Reveal.registerPlugin( 'zoom', RevealZoom );
+
 /*!
 /*!
  * zoom.js 0.3 (modified for use with reveal.js)
  * zoom.js 0.3 (modified for use with reveal.js)
  * http://lab.hakim.se/zoom-js
  * http://lab.hakim.se/zoom-js

+ 1 - 0
test/assets/external-script-a.js

@@ -0,0 +1 @@
+window.externalScriptSequence += 'A';

+ 1 - 0
test/assets/external-script-b.js

@@ -0,0 +1 @@
+window.externalScriptSequence += 'B';

+ 1 - 0
test/assets/external-script-c.js

@@ -0,0 +1 @@
+window.externalScriptSequence += 'C';

+ 1 - 0
test/assets/external-script-d.js

@@ -0,0 +1 @@
+window.externalScriptSequence += 'D';

+ 2 - 0
test/examples/assets/beeping.txt

@@ -0,0 +1,2 @@
+Source: https://freesound.org/people/fennelliott/sounds/379419/
+License: CC0 (public domain)

BIN
test/examples/assets/beeping.wav


+ 5 - 1
test/examples/embedded-media.html

@@ -30,11 +30,15 @@
 					<h2>Empty Slide</h2>
 					<h2>Empty Slide</h2>
 				</section>
 				</section>
 
 
+				<section>
+					<h2>Auto-playing audio</h2>
+					<audio src="assets/beeping.wav" data-autoplay></audio>
+				</section>
+
 			</div>
 			</div>
 
 
 		</div>
 		</div>
 
 
-		<script src="../../lib/js/head.min.js"></script>
 		<script src="../../js/reveal.js"></script>
 		<script src="../../js/reveal.js"></script>
 
 
 		<script>
 		<script>

+ 23 - 3
test/examples/math.html

@@ -82,6 +82,14 @@
 					\]
 					\]
 				</section>
 				</section>
 
 
+				<section>
+					<h3>TeX Macros</h3>
+
+					Here is a common vector space:
+					\[L^2(\R) = \set{u : \R \to \R}{\int_\R |u|^2 &lt; +\infty}\]
+					used in functional analysis.
+				</section>
+
 				<section>
 				<section>
 					<section>
 					<section>
 						<h3>The Lorenz Equations</h3>
 						<h3>The Lorenz Equations</h3>
@@ -153,13 +161,20 @@
 							\]
 							\]
 						</div>
 						</div>
 					</section>
 					</section>
+
+					<section>
+						<h3>TeX Macros</h3>
+
+						Here is a common vector space:
+						\[L^2(\R) = \set{u : \R \to \R}{\int_\R |u|^2 &lt; +\infty}\]
+						used in functional analysis.
+					</section>
 				</section>
 				</section>
 
 
 			</div>
 			</div>
 
 
 		</div>
 		</div>
 
 
-		<script src="../../lib/js/head.min.js"></script>
 		<script src="../../js/reveal.js"></script>
 		<script src="../../js/reveal.js"></script>
 
 
 		<script>
 		<script>
@@ -170,11 +185,16 @@
 
 
 				math: {
 				math: {
 					// mathjax: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js',
 					// mathjax: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js',
-					config: 'TeX-AMS_HTML-full'
+					config: 'TeX-AMS_HTML-full',
+					TeX: {
+						Macros: {
+							R: '\\mathbb{R}',
+							set: [ '\\left\\{#1 \\; ; \\; #2\\right\\}', 2 ]
+						}
+					}
 				},
 				},
 
 
 				dependencies: [
 				dependencies: [
-					{ src: '../../lib/js/classList.js' },
 					{ src: '../../plugin/math/math.js', async: true }
 					{ src: '../../plugin/math/math.js', async: true }
 				]
 				]
 			});
 			});

+ 0 - 1
test/examples/slide-backgrounds.html

@@ -122,7 +122,6 @@
 
 
 		</div>
 		</div>
 
 
-		<script src="../../lib/js/head.min.js"></script>
 		<script src="../../js/reveal.js"></script>
 		<script src="../../js/reveal.js"></script>
 
 
 		<script>
 		<script>

+ 0 - 1
test/examples/slide-transitions.html

@@ -81,7 +81,6 @@
 
 
 		</div>
 		</div>
 
 
-		<script src="../../lib/js/head.min.js"></script>
 		<script src="../../js/reveal.js"></script>
 		<script src="../../js/reveal.js"></script>
 
 
 		<script>
 		<script>

+ 78 - 0
test/test-dependencies-async.html

@@ -0,0 +1,78 @@
+<!doctype html>
+<html lang="en">
+
+	<head>
+		<meta charset="utf-8">
+
+		<title>reveal.js - Test Async Dependencies</title>
+
+		<link rel="stylesheet" href="../css/reveal.css">
+		<link rel="stylesheet" href="qunit-2.5.0.css">
+	</head>
+
+	<body style="overflow: auto;">
+
+		<div id="qunit"></div>
+		<div id="qunit-fixture"></div>
+
+		<div class="reveal" style="display: none;">
+
+			<div class="slides">
+
+				<section>Slide content</section>
+
+			</div>
+
+		</div>
+
+		<script src="../js/reveal.js"></script>
+		<script src="qunit-2.5.0.js"></script>
+
+		<script>
+			var	externalScriptSequence = '';
+			var scriptCount = 0;
+
+			QUnit.config.autostart = false;
+			QUnit.module( 'Async Dependencies' );
+
+			QUnit.test( 'Async scripts are loaded', function( assert ) {
+				assert.expect( 5 );
+				var done = assert.async( 5 );
+
+				function callback( event ) {
+					if( externalScriptSequence.length === 1 ) {
+						assert.ok( externalScriptSequence === 'A', 'first callback was sync script' );
+						done();
+					}
+					else {
+						assert.ok( true, 'async script loaded' );
+						done();
+					}
+
+					if( externalScriptSequence.length === 4 ) {
+						assert.ok( 	externalScriptSequence.indexOf( 'A' ) !== -1 &&
+									externalScriptSequence.indexOf( 'B' ) !== -1 &&
+									externalScriptSequence.indexOf( 'C' ) !== -1 &&
+									externalScriptSequence.indexOf( 'D' ) !== -1, 'four unique scripts were loaded' );
+						done();
+					}
+
+					scriptCount ++;
+				}
+
+				Reveal.initialize({
+					dependencies: [
+						{ src: 'assets/external-script-a.js', async: false, callback: callback },
+						{ src: 'assets/external-script-b.js', async: true, callback: callback },
+						{ src: 'assets/external-script-c.js', async: true, callback: callback },
+						{ src: 'assets/external-script-d.js', async: true, callback: callback }
+					]
+				});
+			});
+
+			QUnit.start();
+
+		</script>
+
+	</body>
+</html>

+ 54 - 0
test/test-dependencies.html

@@ -0,0 +1,54 @@
+<!doctype html>
+<html lang="en">
+
+	<head>
+		<meta charset="utf-8">
+
+		<title>reveal.js - Test Dependencies</title>
+
+		<link rel="stylesheet" href="../css/reveal.css">
+		<link rel="stylesheet" href="qunit-2.5.0.css">
+	</head>
+
+	<body style="overflow: auto;">
+
+		<div id="qunit"></div>
+		<div id="qunit-fixture"></div>
+
+		<div class="reveal" style="display: none;">
+
+			<div class="slides">
+
+				<section>Slide content</section>
+
+			</div>
+
+		</div>
+
+		<script src="../js/reveal.js"></script>
+		<script src="qunit-2.5.0.js"></script>
+
+		<script>
+			window.externalScriptSequence = '';
+
+			Reveal.addEventListener( 'ready', function() {
+
+				QUnit.module( 'Dependencies' );
+
+				QUnit.test( 'Load synchronous scripts', function( assert ) {
+					assert.strictEqual( window.externalScriptSequence, 'ABC', 'Loaded and executed in order' );
+				});
+
+			} );
+
+			Reveal.initialize({
+				dependencies: [
+					{ src: 'assets/external-script-a.js' },
+					{ src: 'assets/external-script-b.js' },
+					{ src: 'assets/external-script-c.js' }
+				]
+			});
+		</script>
+
+	</body>
+</html>

+ 74 - 0
test/test-grid-navigation.html

@@ -0,0 +1,74 @@
+<!doctype html>
+<html lang="en">
+
+	<head>
+		<meta charset="utf-8">
+
+		<title>reveal.js - Test Grid</title>
+
+		<link rel="stylesheet" href="../css/reveal.css">
+		<link rel="stylesheet" href="qunit-2.5.0.css">
+	</head>
+
+	<body style="overflow: auto;">
+
+		<div id="qunit"></div>
+		<div id="qunit-fixture"></div>
+
+		<div class="reveal" style="display: none;">
+
+			<div class="slides">
+
+				<section>0</section>
+				<section>
+					<section>1.1</section>
+					<section>1.2</section>
+					<section>1.3</section>
+					<section>1.4</section>
+				</section>
+				<section>
+					<section>2.1</section>
+					<section>2.2</section>
+					<section>2.3</section>
+					<section>2.4</section>
+				</section>
+
+			</div>
+
+		</div>
+
+		<script src="../js/reveal.js"></script>
+		<script src="qunit-2.5.0.js"></script>
+
+		<script>
+			Reveal.addEventListener( 'ready', function() {
+
+				QUnit.module( 'Grid Navigation' );
+
+				QUnit.test( 'Disabled', function( assert ) {
+					Reveal.right();
+					Reveal.down();
+					Reveal.down();
+					assert.deepEqual( Reveal.getIndices(), { h: 1, v: 2, f: undefined }, 'Correct starting point' );
+					Reveal.right();
+					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: undefined }, 'Moves to top when going to adjacent stack' );
+				});
+
+				QUnit.test( 'Enabled', function( assert ) {
+					Reveal.configure({ navigationMode: 'grid' });
+					Reveal.slide( 0, 0 );
+					Reveal.right();
+					Reveal.down();
+					Reveal.down();
+					assert.deepEqual( Reveal.getIndices(), { h: 1, v: 2, f: undefined }, 'Correct starting point' );
+					Reveal.right();
+					assert.deepEqual( Reveal.getIndices(), { h: 2, v: 2, f: undefined }, 'Remains at same vertical index when going to adjacent stack' );
+				});
+
+			} );
+
+			Reveal.initialize();
+		</script>
+
+	</body>
+</html>

+ 1 - 3
test/test-markdown-element-attributes.html

@@ -66,8 +66,7 @@
 
 
 						Test
 						Test
 
 
-						![Example Picture](examples/assets/image2.png)
-						<!-- {_class="reveal stretch"} -->
+						![Example Picture](examples/assets/image2.png) <!-- {_class="reveal stretch"} -->
 
 
 					</script>
 					</script>
 				</section>
 				</section>
@@ -122,7 +121,6 @@
 
 
 		</div>
 		</div>
 
 
-		<script src="../lib/js/head.min.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="../plugin/markdown/marked.js"></script>
 		<script src="../plugin/markdown/marked.js"></script>
 		<script src="../plugin/markdown/markdown.js"></script>
 		<script src="../plugin/markdown/markdown.js"></script>

+ 5 - 4
test/test-markdown-external.html

@@ -13,24 +13,25 @@
 	<body style="overflow: auto;">
 	<body style="overflow: auto;">
 
 
 		<div id="qunit"></div>
 		<div id="qunit"></div>
-  		<div id="qunit-fixture"></div>
+		<div id="qunit-fixture"></div>
 
 
 		<div class="reveal" style="display: none;">
 		<div class="reveal" style="display: none;">
 
 
 			<div class="slides">
 			<div class="slides">
-				<section data-markdown="simple.md" data-separator="^\r?\n\r?\n\r?\n" data-separator-vertical="^\r?\n\r?\n"></section>
+				<!-- <section data-markdown="simple.md" data-separator="^\r?\n\r?\n\r?\n" data-separator-vertical="^\r?\n\r?\n"></section> -->
 			</div>
 			</div>
 
 
 		</div>
 		</div>
 
 
-		<script src="../lib/js/head.min.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="../plugin/highlight/highlight.js"></script>
 		<script src="../plugin/highlight/highlight.js"></script>
 		<script src="../plugin/markdown/marked.js"></script>
 		<script src="../plugin/markdown/marked.js"></script>
 		<script src="../plugin/markdown/markdown.js"></script>
 		<script src="../plugin/markdown/markdown.js"></script>
 		<script src="qunit-2.5.0.js"></script>
 		<script src="qunit-2.5.0.js"></script>
 
 
-		<script src="test-markdown-external.js"></script>
+		<!-- <script src="test-markdown-external.js"></script> -->
+
+		<!-- Test disabled 28/2/2019 by Hakim – Markdown plugin needs to be updated to load extenal files asycnhronously -->
 
 
 	</body>
 	</body>
 </html>
 </html>

+ 0 - 1
test/test-markdown-options.html

@@ -31,7 +31,6 @@
 
 
 		</div>
 		</div>
 
 
-		<script src="../lib/js/head.min.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="qunit-2.5.0.js"></script>
 		<script src="qunit-2.5.0.js"></script>
 
 

+ 0 - 1
test/test-markdown-slide-attributes.html

@@ -116,7 +116,6 @@
 
 
 		</div>
 		</div>
 
 
-		<script src="../lib/js/head.min.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="../plugin/markdown/marked.js"></script>
 		<script src="../plugin/markdown/marked.js"></script>
 		<script src="../plugin/markdown/markdown.js"></script>
 		<script src="../plugin/markdown/markdown.js"></script>

+ 0 - 1
test/test-markdown.html

@@ -40,7 +40,6 @@
 
 
 		</div>
 		</div>
 
 
-		<script src="../lib/js/head.min.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="../plugin/markdown/marked.js"></script>
 		<script src="../plugin/markdown/marked.js"></script>
 		<script src="../plugin/markdown/markdown.js"></script>
 		<script src="../plugin/markdown/markdown.js"></script>

+ 1 - 2
test/test-pdf.html

@@ -22,7 +22,7 @@
 
 
 				<section>
 				<section>
 					<h1>1</h1>
 					<h1>1</h1>
-					<img data-src="fake-url.png">
+					<img data-src="">
 				</section>
 				</section>
 
 
 				<section>
 				<section>
@@ -73,7 +73,6 @@
 
 
 		</div>
 		</div>
 
 
-		<script src="../lib/js/head.min.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="qunit-2.5.0.js"></script>
 		<script src="qunit-2.5.0.js"></script>
 
 

+ 92 - 0
test/test-plugins.html

@@ -0,0 +1,92 @@
+<!doctype html>
+<html lang="en">
+
+	<head>
+		<meta charset="utf-8">
+
+		<title>reveal.js - Test Plugins</title>
+
+		<link rel="stylesheet" href="../css/reveal.css">
+		<link rel="stylesheet" href="qunit-2.5.0.css">
+	</head>
+
+	<body style="overflow: auto;">
+
+		<div id="qunit"></div>
+		<div id="qunit-fixture"></div>
+
+		<div class="reveal" style="display: none;">
+
+			<div class="slides">
+
+				<section>Slide content</section>
+
+			</div>
+
+		</div>
+
+		<script src="../js/reveal.js"></script>
+		<script src="qunit-2.5.0.js"></script>
+
+		<script>
+
+			QUnit.module( 'Plugins' );
+
+			var initCounter = { PluginB: 0, PluginC: 0, PluginD: 0 };
+
+			// Plugin with no init method
+			var PluginA = {};
+
+			// Plugin with init method
+			var PluginB = { init: function() {
+				initCounter['PluginB'] += 1;
+			} };
+
+			// Async plugin with init method
+			var PluginC = { init: function() {
+				return new Promise(function( resolve ) {
+					setTimeout( () => {
+						initCounter['PluginC'] += 1;
+						resolve();
+					}, 1000 );
+				});
+			} };
+
+			// Plugin initialized after reveal.js is ready
+			var PluginD = { init: function() {
+				initCounter['PluginD'] += 1;
+			} };
+
+			Reveal.registerPlugin( 'PluginA', PluginA );
+			Reveal.registerPlugin( 'PluginB', PluginB );
+			Reveal.registerPlugin( 'PluginC', PluginC );
+
+			Reveal.initialize();
+
+			QUnit.test( 'Can initialize synchronously', function( assert ) {
+				assert.strictEqual( initCounter['PluginB'], 1 );
+
+				Reveal.registerPlugin( 'PluginB', PluginB );
+
+				assert.strictEqual( initCounter['PluginB'], 1, 'prevents duplicate registration' );
+			});
+
+			QUnit.test( 'Can initialie asynchronously', function( assert ) {
+				assert.expect( 3 );
+				var done = assert.async( 2 );
+
+				assert.strictEqual( initCounter['PluginC'], 0, 'async plugin not immediately initialized' );
+
+				Reveal.addEventListener( 'ready', function() {
+					assert.strictEqual( initCounter['PluginC'], 1, 'finsihed initializing when reveal.js dispatches "ready"' );
+					done();
+
+					Reveal.registerPlugin( 'PluginD', PluginD );
+					assert.strictEqual( initCounter['PluginD'], 1, 'plugin registered after reveal.js is ready still initiailizes' );
+					done();
+				});
+			} );
+		</script>
+
+	</body>
+</html>

+ 139 - 0
test/test-state.html

@@ -0,0 +1,139 @@
+<!doctype html>
+<html lang="en">
+
+	<head>
+		<meta charset="utf-8">
+
+		<title>reveal.js - Test State</title>
+
+		<link rel="stylesheet" href="../css/reveal.css">
+		<link rel="stylesheet" href="qunit-2.5.0.css">
+	</head>
+
+	<body style="overflow: auto;">
+
+		<div id="qunit"></div>
+		<div id="qunit-fixture"></div>
+
+		<div class="reveal" style="display: none;">
+
+			<div class="slides">
+
+				<section>No state</section>
+				<section id="slide2" data-state="state1">State 1</section>
+				<section data-state="state1">State 1</section>
+				<section data-state="state2">State 2</section>
+				<section>
+					<section>No state</section>
+					<section data-state="state1">State 1</section>
+					<section data-state="state3">State 3</section>
+					<section>No state</section>
+				</section>
+				<section>No state</section>
+
+			</div>
+
+		</div>
+
+		<script src="../js/reveal.js"></script>
+		<script src="qunit-2.5.0.js"></script>
+
+		<script>
+
+
+			Reveal.addEventListener( 'ready', function() {
+
+				QUnit.module( 'State' );
+
+				QUnit.test( 'Fire events when changing slide', function( assert ) {
+					assert.expect( 2 );
+					var state1 = assert.async();
+					var state2 = assert.async();
+
+					var _onState1 = function( event ) {
+						assert.ok( true, 'state1 fired' );
+						state1();
+					}
+
+					var _onState2 = function( event ) {
+						assert.ok( true, 'state2 fired' );
+						state2();
+					}
+
+					Reveal.addEventListener( 'state1', _onState1 );
+					Reveal.addEventListener( 'state2', _onState2 );
+
+					Reveal.slide( 1 );
+					Reveal.slide( 3 );
+
+					Reveal.removeEventListener( 'state1', _onState1 );
+					Reveal.removeEventListener( 'state2', _onState2 );
+				});
+
+				QUnit.test( 'Fire state events for vertical slides', function( assert ) {
+					assert.expect( 2 );
+					var done = assert.async( 2 );
+
+					var _onState1 = function( event ) {
+						assert.ok( true, 'state1 fired' );
+						done();
+					}
+
+					var _onState3 = function( event ) {
+						assert.ok( true, 'state3 fired' );
+						done();
+					}
+
+					Reveal.addEventListener( 'state1', _onState1 );
+					Reveal.addEventListener( 'state3', _onState3 );
+
+					Reveal.slide( 0 );
+					Reveal.slide( 4, 1 );
+					Reveal.slide( 4, 2 );
+
+					Reveal.removeEventListener( 'state1', _onState1 );
+					Reveal.removeEventListener( 'state3', _onState3 );
+				});
+
+				QUnit.test( 'No events if state remains unchanged', function( assert ) {
+					var stateChanges = 0;
+
+					var _onEvent = function( event ) {
+						stateChanges += 1;
+					}
+
+					Reveal.addEventListener( 'state1', _onEvent );
+
+					Reveal.slide( 0 );      // no state
+					Reveal.slide( 1 );      // state1
+					Reveal.slide( 2 );      // state1
+					Reveal.prev();          // state1
+					Reveal.next();          // state1
+					Reveal.slide( 4, 1 );   // state1
+					Reveal.slide( 0 );      // no state
+
+					Reveal.removeEventListener( 'state1', _onEvent );
+
+					assert.strictEqual( stateChanges, 1, 'no event was fired when going to slide with same state' );
+				});
+
+				QUnit.test( 'Event order', function( assert ) {
+					var _onEvent = function( event ) {
+						assert.ok( Reveal.getCurrentSlide() == document.querySelector( '#slide2' ), 'correct current slide immediately after state event' );
+					}
+
+					Reveal.addEventListener( 'state1', _onEvent );
+
+					Reveal.slide( 0 );
+					Reveal.slide( 1 );
+
+					Reveal.removeEventListener( 'state1', _onEvent );
+				});
+
+			} );
+
+			Reveal.initialize();
+		</script>
+
+	</body>
+</html>

+ 3 - 4
test/test.html

@@ -21,9 +21,9 @@
 
 
 				<section data-background-image="examples/assets/image1.png">
 				<section data-background-image="examples/assets/image1.png">
 					<h1>1</h1>
 					<h1>1</h1>
-					<img data-src="fake-url.png">
-					<video data-src="fake-url.mp4"></video>
-					<audio data-src="fake-url.mp3"></audio>
+					<img data-src="">
+					<video data-src=""></video>
+					<audio data-src=""></audio>
 					<aside class="notes">speaker notes 1</aside>
 					<aside class="notes">speaker notes 1</aside>
 				</section>
 				</section>
 
 
@@ -76,7 +76,6 @@
 
 
 		</div>
 		</div>
 
 
-		<script src="../lib/js/head.min.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="../js/reveal.js"></script>
 		<script src="qunit-2.5.0.js"></script>
 		<script src="qunit-2.5.0.js"></script>
 
 

+ 5 - 0
test/test.js

@@ -262,6 +262,7 @@ Reveal.addEventListener( 'ready', function() {
 
 
 	QUnit.test( 'Current fragment', function( assert ) {
 	QUnit.test( 'Current fragment', function( assert ) {
 		var fragmentSlide = document.querySelector( '#fragment-slides>section:nth-child(1)' );
 		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 );
 		Reveal.slide( 2, 0 );
 		assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 0, 'no current fragment at index -1' );
 		assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 0, 'no current fragment at index -1' );
@@ -274,6 +275,10 @@ Reveal.addEventListener( 'ready', function() {
 
 
 		Reveal.slide( 3, 0, 0 );
 		Reveal.slide( 3, 0, 0 );
 		assert.strictEqual( fragmentSlide.querySelectorAll( '.fragment.current-fragment' ).length, 0, 'no current fragment when navigating to next slide' );
 		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 ) {
 	QUnit.test( 'Stepping through fragments', function( assert ) {