taking a stab at a presenter notes server
authorRebecca Murphey <rmurphey@gmail.com>
Fri, 8 Jun 2012 03:21:14 +0000 (23:21 -0400)
committerRebecca Murphey <rmurphey@gmail.com>
Fri, 8 Jun 2012 03:21:14 +0000 (23:21 -0400)
.gitignore
README.md
css/main.css
index.html
js/slidenotes.js [new file with mode: 0644]
package.json [new file with mode: 0644]
slidenotes/index.js [new file with mode: 0644]
slidenotes/notes.html [new file with mode: 0644]

index dec0ea42485f2dc4354b9e270b43b73aa2d00fdb..34058c44cda2d2548235c3fd1846de5b0931e26d 100644 (file)
@@ -2,3 +2,4 @@
 .svn
 log/*.log
 tmp/**
+node_modules/
index 466dbb07bad9f63c61dcff500a1971a6deec1c21..f84ee663ff24dd5d939cbf4d7ae884ccc0dfafcc 100644 (file)
--- a/README.md
+++ b/README.md
@@ -102,6 +102,27 @@ Reveal.addEventListener( 'fragmenthidden', function( event ) {
 } );
 ```
 
+## Speaker Notes
+
+If you're interested in using speaker notes, reveal.js comes with a Node server that allows you to deliver your presentation in one browser while viewing speaker notes in another. 
+
+To include speaker notes in your presentation, simply add an `<aside class="notes">` element to any slide. These notes will be hidden in the main presentation view.
+
+To use the speaker notes server, your `index.html` will need to include script tags for `socket.io/socket.io.js` and `js/slidenotes.js`. If you don't want to use the speaker notes server, you can safely remove these script tags, but they are included by default. 
+
+You'll also need to [install Node.js](http://nodejs.org/); then, install the server dependencies by running `npm install`.
+
+Once Node.js and the dependencies are installed, run the following command from the root directory:
+
+               node slidenotes
+
+By default, the slides will be served at [localhost:1947](http://localhost:1947).
+
+You can change the appearance of the speaker notes by editing the file at `slidenotes/notes.html`.
+
+### Known Issues
+
+- The notes page is supposed to show the current slide and the next slide, but when it first starts, it always shows the first slide in both positions. 
 
 ## Examples
 
index 41c06cf64670437e02eaff04495bc08472eadf10..00f06ff45cc22c85d4ea209c404f22ab2992d6ea 100644 (file)
@@ -942,6 +942,10 @@ body {
        background: rgba( 0, 0, 0, 0.6 );
 }
 
+/*********************************************
+ * SPEAKER NOTES
+ *********************************************/
 
-
-
+.reveal aside.notes {
+       display: none;
+}
index a68dd6a72e41e24aea4e2037a8d0b1262abc980c..d4420ac22168ba55891d8ebc96605af3370eeda2 100644 (file)
                                        <p>
                                                <i><small>- <a href="http://hakim.se">Hakim El Hattab</a> / <a href="http://twitter.com/hakimel">@hakimel</a></small></i>
                                        </p>
+
+                                       <aside class="notes">
+                                               Oh hey, these are some notes. They'll be hidden in your presentation, but you can see them if you run the speaker notes server.
+                                       </aside>
                                </section>
                                
                                <!-- Example of nested vertical slides -->
@@ -268,6 +272,7 @@ linkify( 'a' );
                <script>
                        // Parse the query string into a key/value object
                        var query = {};
+
                        location.search.replace( /[A-Z0-9]+?=(\w*)/gi, function(a) {
                                query[ a.split( '=' ).shift() ] = a.split( '=' ).pop();
                        } );
@@ -311,5 +316,8 @@ linkify( 'a' );
                        hljs.initHighlightingOnLoad();
                </script>
 
+               <!-- the next two lines enable the speaker notes server -->
+               <script src="socket.io/socket.io.js"></script>
+               <script src="js/slidenotes.js"></script>
        </body>
 </html>
\ No newline at end of file
diff --git a/js/slidenotes.js b/js/slidenotes.js
new file mode 100644 (file)
index 0000000..9a4f152
--- /dev/null
@@ -0,0 +1,32 @@
+(function() {
+  // don't emit events from inside the previews themselves
+  var qs = window.location.href.split('?');
+  if (qs.length > 1 && qs[1].match('receiver')) { return; }
+
+  var socket = io.connect('http://localhost:1947');
+  
+  Reveal.addEventListener( 'slidechanged', function( event ) {
+    var nextindexh;
+    var nextindexv;
+    var slideElement = event.currentSlide;
+
+    if (slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION') {
+      nextindexh = event.indexh;
+      nextindexv = event.indexv + 1;
+    } else {
+      nextindexh = event.indexh + 1;
+      nextindexv = 0;
+    }
+
+    var notes = slideElement.querySelector('aside.notes');
+    var slideData = {
+      notes : notes ? notes.innerHTML : '',
+      indexh : event.indexh,
+      indexv : event.indexv,
+      nextindexh : nextindexh,
+      nextindexv : nextindexv
+    };
+
+    socket.emit('slidechanged', slideData);
+  } );
+}());
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644 (file)
index 0000000..68514f4
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "author": "Hakim El Hattab",
+  "name": "Reveal.js",
+  "description": "HTML5 Slideware with Presenter Notes",
+  "version": "1.5.0",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/hakimel/reveal.js.git"
+  },
+  "engines": {
+    "node": "~0.6.8"
+  },
+  "dependencies": {
+    "express" : "2.5.9",
+    "socket.io" : "0.9.6"
+  },
+  "devDependencies": {}
+}
diff --git a/slidenotes/index.js b/slidenotes/index.js
new file mode 100644 (file)
index 0000000..fb6baf8
--- /dev/null
@@ -0,0 +1,41 @@
+var express   = require('express');
+var fs        = require('fs');
+var io        = require('socket.io');
+var _         = require('underscore');
+
+var app       = express.createServer();
+var staticDir = express.static;
+
+io            = io.listen(app);
+
+var opts = {
+  port :      1947,
+  baseDir :   __dirname + '/../'
+};
+
+io.sockets.on('connection', function(socket) {
+  socket.on('slidechanged', function(slideData) {
+    socket.broadcast.emit('slidedata', slideData);
+  });
+});
+
+app.configure(function() {
+  [ 'css', 'assets', 'js', 'lib' ].forEach(function(dir) {
+    app.use('/' + dir, staticDir(opts.baseDir + dir));
+  });
+});
+
+app.get("/", function(req, res) {
+  fs.createReadStream(opts.baseDir + '/index.html').pipe(res);
+});
+
+app.get("/_notes", function(req, res) {
+  fs.createReadStream(opts.baseDir + 'slidenotes/notes.html').pipe(res);
+});
+
+// Actually listen
+app.listen(opts.port || null);
+
+console.log("Your slides are at http://localhost" + (opts.port ? (':' + opts.port) : ''));
+console.log("Your notes are at http://localhost" + (opts.port ? (':' + opts.port) : '') + '/_notes');
+console.log("Advance through your slides and your speaker notes will advance automatically");
\ No newline at end of file
diff --git a/slidenotes/notes.html b/slidenotes/notes.html
new file mode 100644 (file)
index 0000000..955dce1
--- /dev/null
@@ -0,0 +1,87 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+
+    <title>Slide Notes</title>
+
+    <meta name="description" content="">
+    <meta name="author" content="Rebecca Murphey">
+
+    <meta name="apple-mobile-web-app-capable" content="yes" />
+    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
+
+    <style>
+      #notes {
+        font-family: Helvetica;
+        font-size: 24px;
+        width: 640px;
+      }
+
+      #wrap-slides {
+        width: 640px;
+        height: 512px;
+        float: left;
+      }
+
+      #slides { 
+        width: 1280px; 
+        height: 1024px; 
+        border: 1px solid black; 
+        -moz-transform: scale(0.5);
+        -moz-transform-origin: 0 0;
+        -o-transform: scale(0.5);
+        -o-transform-origin: 0 0;
+        -webkit-transform: scale(0.5);
+        -webkit-transform-origin: 0 0;      
+      }
+
+      #wrap-next-slide {
+        width: 320px;
+        height: 256px;
+        float: left;
+        margin: 0 0 0 50px;
+      }
+
+      #next-slide { 
+        width: 1280px; 
+        height: 1024px; 
+        border: 1px solid black; 
+        -moz-transform: scale(0.25);
+        -moz-transform-origin: 0 0;
+        -o-transform: scale(0.25);
+        -o-transform-origin: 0 0;
+        -webkit-transform: scale(0.25);
+        -webkit-transform-origin: 0 0;      
+      }
+    </style>
+  </head>
+
+  <body>
+
+    <div id="wrap-slides">
+      <iframe src="/?receiver" width="1280" height="1024" id="slides"></iframe>
+    </div>
+
+    <div id="wrap-next-slide">
+      <iframe src="/?receiver" width="640" height="512" id="next-slide"></iframe>
+    </div>
+    <div id="notes"></div>
+
+    <script src="socket.io/socket.io.js"></script>
+
+    <script>
+    var socket = io.connect('http://localhost:1947');
+    var notes = document.getElementById('notes');
+    var slides = document.getElementById('slides');
+    var nextSlide = document.getElementById('next-slide');
+
+    socket.on('slidedata', function(data) {
+      notes.innerHTML = data.notes;
+      slides.contentWindow.Reveal.navigateTo(data.indexh, data.indexv);
+      nextSlide.contentWindow.Reveal.navigateTo(data.nextindexh, data.nextindexv);
+    });
+    </script>
+
+  </body>
+</html>