new postMessage-based notes plugin, moved node-based notes to notes-server (#190)
This commit is contained in:
		
							
								
								
									
										33
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								README.md
									
									
									
									
									
								
							| @@ -105,10 +105,9 @@ Reveal.initialize({ | |||||||
| 		{ src: 'lib/js/data-markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, | 		{ 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]' ); } }, | 		{ src: 'lib/js/showdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, | ||||||
| 		// Zoom in and out with Alt+click | 		// Zoom in and out with Alt+click | ||||||
| 		{ src: 'plugin/zoom-js/zoom.js', condition: function() { return !!document.body.classList; } }, | 		{ src: 'plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } }, | ||||||
| 		// Speaker notes support | 		// Speaker notes | ||||||
| 		{ src: 'plugin/speakernotes/client.js', async: true, condition: function() { return window.location.host === 'localhost:1947'; } }, | 		{ src: 'plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } } | ||||||
| 		{ src: '/socket.io/socket.io.js', async: true, condition: function() { return window.location.host === 'localhost:1947'; } }, |  | ||||||
| 	] | 	] | ||||||
| }); | }); | ||||||
| ``` | ``` | ||||||
| @@ -230,27 +229,23 @@ Here's an example of an exported presentation that's been uploaded to SlideShare | |||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Speaker Notes | ## 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.  | 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. | ||||||
|  |  | ||||||
| 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. | Notes are written using the following markup structure: | ||||||
|  |  | ||||||
| It's also possible to write your notes with Markdown. To enable Markdown, add the ```data-markdown``` attribute to your note ```<aside>``` elements. | ```html | ||||||
|  | <section> | ||||||
|  | 	<h2>Some Slide</h2> | ||||||
|  |  | ||||||
| You'll also need to [install Node.js](http://nodejs.org/); then, install the server dependencies by running `npm install`. | 	<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> | ||||||
|  | ``` | ||||||
|  |  | ||||||
| Once Node.js and the dependencies are installed, run the following command from the root directory: |  | ||||||
|  |  | ||||||
| 		node plugin/speakernotes |  | ||||||
|  |  | ||||||
| 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 `plugin/speakernotes/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.  |  | ||||||
|  |  | ||||||
| ## Folder Structure | ## Folder Structure | ||||||
| - **css/** Core styles without which the project does not function | - **css/** Core styles without which the project does not function | ||||||
|   | |||||||
| @@ -51,7 +51,7 @@ | |||||||
| 					</p> | 					</p> | ||||||
|  |  | ||||||
| 					<aside class="notes"> | 					<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> | 					</aside> | ||||||
| 				</section> | 				</section> | ||||||
| 				 | 				 | ||||||
| @@ -356,12 +356,10 @@ function linkify( selector ) { | |||||||
| 					{ src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } }, | 					{ 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/showdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } }, | ||||||
| 					{ src: 'lib/js/data-markdown.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: 'plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } }, | ||||||
| 					{ src: '/socket.io/socket.io.js', async: true, condition: function() { return window.location.host === 'localhost:1947'; } }, | 					{ src: 'plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } } | ||||||
| 					{ src: 'plugin/speakernotes/client.js', async: true, condition: function() { return window.location.host === 'localhost:1947'; } } |  | ||||||
| 				] | 				] | ||||||
| 			}); | 			}); | ||||||
|  |  | ||||||
| 		</script> | 		</script> | ||||||
|  |  | ||||||
| 	</body> | 	</body> | ||||||
|   | |||||||
| @@ -32,12 +32,12 @@ app.get("/", function(req, res) { | |||||||
| 
 | 
 | ||||||
| app.get("/notes/:socketId", 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(), { | 		res.send(Mustache.to_html(data.toString(), { | ||||||
| 			socketId : req.params.socketId | 			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
 | // Actually listen
 | ||||||
							
								
								
									
										157
									
								
								plugin/notes/notes.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								plugin/notes/notes.html
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								plugin/notes/notes.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								plugin/notes/notes.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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 } | ||||||
|  | })(); | ||||||
		Reference in New Issue
	
	Block a user