Explorar el Código

new postMessage-based notes plugin, moved node-based notes to notes-server (#190)

Hakim El Hattab hace 12 años
padre
commit
c6f8a44edf

+ 14 - 19
README.md

@@ -105,10 +105,9 @@ Reveal.initialize({
 		{ src: 'lib/js/data-markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
 		{ src: 'lib/js/showdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
 		// Zoom in and out with Alt+click
-		{ src: 'plugin/zoom-js/zoom.js', condition: function() { return !!document.body.classList; } },
-		// Speaker notes support
-		{ src: 'plugin/speakernotes/client.js', async: true, condition: function() { return window.location.host === 'localhost:1947'; } },
-		{ src: '/socket.io/socket.io.js', async: true, condition: function() { return window.location.host === 'localhost:1947'; } },
+		{ src: 'plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } },
+		// Speaker notes
+		{ src: 'plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } }
 	]
 });
 ```
@@ -230,27 +229,23 @@ Here's an example of an exported presentation that's been uploaded to SlideShare
 
 ![Chrome Print Settings](https://s3.amazonaws.com/hakim-static/reveal-js/pdf-print-settings.png)
 
-## 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.
-
-It's also possible to write your notes with Markdown. To enable Markdown, add the ```data-markdown``` attribute to your note ```<aside>``` elements.
-
-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:
+## Speaker Notes
 
-		node plugin/speakernotes
+reveal.js comes with a speaker notes plugin which can be used to present per-slide notes in a separate browser window. The notes window also gives you a preview of the next upcoming slide so it may be helpful even if you haven't written any notes. Append ```?notes``` to presentation URL or press the 's' key on your keyboard to open the notes window.
 
-By default, the slides will be served at [localhost:1947](http://localhost:1947).
+Notes are written using the following markup structure:
 
-You can change the appearance of the speaker notes by editing the file at `plugin/speakernotes/notes.html`.	
+```html
+<section>
+	<h2>Some Slide</h2>
 
-### Known Issues
+	<aside class="notes">
+		Oh hey, these are some notes. They'll be hidden in your presentation, but you can see them if you open the speaker notes window (hit 's' on your keyboard).
+	</aside>
+</section>
+```
 
-- 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. 
 
 ## Folder Structure
 - **css/** Core styles without which the project does not function

+ 3 - 5
index.html

@@ -51,7 +51,7 @@
 					</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.
+						Oh hey, these are some notes. They'll be hidden in your presentation, but you can see them if you open the speaker notes window (hit 's' on your keyboard).
 					</aside>
 				</section>
 				
@@ -356,12 +356,10 @@ function linkify( selector ) {
 					{ src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } },
 					{ src: 'lib/js/showdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
 					{ src: 'lib/js/data-markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
-					{ src: 'plugin/zoom-js/zoom.js', condition: function() { return !!document.body.classList; } },
-					{ src: '/socket.io/socket.io.js', async: true, condition: function() { return window.location.host === 'localhost:1947'; } },
-					{ src: 'plugin/speakernotes/client.js', async: true, condition: function() { return window.location.host === 'localhost:1947'; } }
+					{ src: 'plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } },
+					{ src: 'plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } }
 				]
 			});
-
 		</script>
 
 	</body>

+ 1 - 1
plugin/speakernotes/client.js → plugin/notes-server/client.js

@@ -35,4 +35,4 @@
 
 		socket.emit('slidechanged', slideData);
 	} );
-}());
+}());

+ 3 - 3
plugin/speakernotes/index.js → plugin/notes-server/index.js

@@ -32,12 +32,12 @@ app.get("/", function(req, res) {
 
 app.get("/notes/:socketId", function(req, res) {
 
-	fs.readFile(opts.baseDir + 'plugin/speakernotes/notes.html', function(err, data) {
+	fs.readFile(opts.baseDir + 'plugin/notes-server/notes.html', function(err, data) {
 		res.send(Mustache.to_html(data.toString(), {
 			socketId : req.params.socketId
 		}));
 	});
-	// fs.createReadStream(opts.baseDir + 'speakernotes/notes.html').pipe(res);
+	// fs.createReadStream(opts.baseDir + 'notes-server/notes.html').pipe(res);
 });
 
 // Actually listen
@@ -52,4 +52,4 @@ var slidesLocation = "http://localhost" + ( opts.port ? ( ':' + opts.port ) : ''
 console.log( brown + "reveal.js - Speaker Notes" + reset );
 console.log( "1. Open the slides at " + green + slidesLocation + reset );
 console.log( "2. Click on the link your JS console to go to the notes page" );
-console.log( "3. Advance through your slides and your notes will advance automatically" );
+console.log( "3. Advance through your slides and your notes will advance automatically" );

+ 1 - 1
plugin/speakernotes/notes.html → plugin/notes-server/notes.html

@@ -125,4 +125,4 @@
 		</script>
 
 	</body>
-</html>
+</html>

+ 157 - 0
plugin/notes/notes.html

@@ -0,0 +1,157 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8">
+
+		<title>reveal.js - Slide Notes</title>
+
+		<style>
+			body {
+				font-family: Helvetica;
+			}
+
+			#notes {
+				font-size: 24px;
+				width: 640px;
+				margin-top: 5px;
+			}
+
+			#wrap-current-slide {
+				width: 640px;
+				height: 512px;
+				float: left;
+				overflow: hidden;
+			}
+
+			#current-slide {
+				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);
+						 -o-transform: scale(0.5);
+								transform: scale(0.5);
+			}
+
+			#wrap-next-slide {
+				width: 448px;
+				height: 358px;
+				float: left;
+				margin: 0 0 0 10px;
+				overflow: hidden;
+			}
+
+			#next-slide {
+				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.35);
+					 -moz-transform: scale(0.35);
+						-ms-transform: scale(0.35);
+						 -o-transform: scale(0.35);
+								transform: scale(0.35);
+			}
+
+			.slides {
+				position: relative;
+				margin-bottom: 10px;
+				border: 1px solid black;
+				border-radius: 2px;
+				background: rgb(28, 30, 32);
+			}
+
+			.slides span {
+				position: absolute;
+				top: 3px;
+				left: 3px;
+				font-weight: bold;
+				font-size: 14px;
+				color: rgba( 255, 255, 255, 0.9 );
+			}
+		</style>
+	</head>
+
+	<body>
+
+		<div id="wrap-current-slide" class="slides">
+			<iframe src="../../index.html" width="1280" height="1024" id="current-slide"></iframe>
+		</div>
+
+		<div id="wrap-next-slide" class="slides">
+			<iframe src="../../index.html" width="640" height="512" id="next-slide"></iframe>
+			<span>UPCOMING:</span>
+		</div>
+		<div id="notes"></div>
+
+		<script src="../../lib/js/showdown.js"></script>
+		<script>
+			window.addEventListener( 'load', function() {
+
+				(function( window, undefined ) {
+					var notes = document.getElementById( 'notes' ),
+						currentSlide = document.getElementById( 'current-slide' ),
+						nextSlide = document.getElementById( 'next-slide' );
+
+					window.addEventListener( 'message', function( event ) {
+						var data = JSON.parse( event.data );
+
+						if( data.markdown ) {
+							notes.innerHTML = (new Showdown.converter()).makeHtml( data.notes );
+						}
+						else {
+							notes.innerHTML = data.notes;
+						}
+
+						// Kill the slide listeners while responding to the event
+						removeSlideListeners();
+
+						// Update the note slides
+						currentSlide.contentWindow.Reveal.slide( data.indexh, data.indexv );
+						nextSlide.contentWindow.Reveal.slide( data.nextindexh, data.nextindexv );
+
+						// Resume listening on the next cycle
+						setTimeout( addSlideListeners, 1 );
+
+					}, false );
+
+					function addSlideListeners() {
+						currentSlide.contentWindow.Reveal.addEventListener( 'slidechanged', onNotesSlideChange, false );
+						nextSlide.contentWindow.Reveal.addEventListener( 'slidechanged', onNotesSlideChange, false );
+					}
+
+					function removeSlideListeners() {
+						currentSlide.contentWindow.Reveal.removeEventListener( 'slidechanged', onNotesSlideChange, false );
+						nextSlide.contentWindow.Reveal.removeEventListener( 'slidechanged', onNotesSlideChange, false );
+					}
+
+					function onNotesSlideChange( event ) {
+						window.opener.postMessage( JSON.stringify({
+							indexh : event.indexh,
+							indexv : event.indexv
+						}), '*' );
+					}
+
+					addSlideListeners();
+
+				})( window );
+
+			}, false );
+
+		</script>
+	</body>
+</html>

+ 74 - 0
plugin/notes/notes.js

@@ -0,0 +1,74 @@
+/**
+ * Handles opening of and synchronization with the reveal.js
+ * notes window.
+ */
+var RevealNotes = (function() {
+
+	function openNotes() {
+		var notesPopup = window.open( 'plugin/notes/notes.html', 'reveal.js - Notes', 'width=1120,height=850' );
+
+		Reveal.addEventListener( 'slidechanged', post );
+
+		// Posts the current slide data to the notes window
+		function post() {
+			var slideElement = Reveal.getCurrentSlide(),
+				indexh = Reveal.getIndices().h,
+				indexv = Reveal.getIndices().v,
+				nextindexh,
+				nextindexv;
+
+			if( slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION' ) {
+				nextindexh = indexh;
+				nextindexv = indexv + 1;
+			} else {
+				nextindexh = indexh + 1;
+				nextindexv = 0;
+			}
+
+			var notes = slideElement.querySelector( 'aside.notes' );
+
+			var slideData = {
+				notes : notes ? notes.innerHTML : '',
+				indexh : indexh,
+				indexv : indexv,
+				nextindexh : nextindexh,
+				nextindexv : nextindexv,
+				markdown : notes ? typeof notes.getAttribute( 'data-markdown' ) === 'string' : false
+			};
+
+			notesPopup.postMessage( JSON.stringify( slideData ), '*' );
+		}
+
+		// The main presentation is kept in sync when navigating the 
+		// note slides so that the popup may be used as a remote
+		window.addEventListener( 'message', function( event ) {
+			var data = JSON.parse( event.data );
+
+			if( data && typeof data.indexh === 'number' && typeof data.indexv === 'number' ) {
+				Reveal.slide( data.indexh, data.indexv );
+			}
+		} );
+
+		// Navigate to the current slide when the notes are loaded
+		notesPopup.addEventListener( 'load', post, false );
+	}
+
+	// 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
+	document.addEventListener( 'keydown', function( event ) {
+		// Disregard the event if the target is editable or a 
+		// modifier is present
+		if ( document.querySelector( ':focus' ) !== null || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ) return;
+
+		if( event.keyCode === 83 ) {
+			event.preventDefault();
+			openNotes();
+		}
+	}, false );
+
+	return { open: openNotes }
+})();