Forráskód Böngészése

fiv out of sync speaker view after presentation reloads #2822 #3032

hakimel 3 éve
szülő
commit
ff20051861
4 módosított fájl, 163 hozzáadás és 110 törlés
  1. 0 0
      plugin/notes/notes.esm.js
  2. 0 0
      plugin/notes/notes.js
  3. 150 110
      plugin/notes/plugin.js
  4. 13 0
      plugin/notes/speaker-view.html

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
plugin/notes/notes.esm.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
plugin/notes/notes.js


+ 150 - 110
plugin/notes/plugin.js

@@ -15,141 +15,167 @@ import marked from 'marked';
  */
 const Plugin = () => {
 
-    let popup = null;
-
-    let deck;
+	let connectInterval;
+	let speakerWindow = null;
+	let deck;
+
+	/**
+	 * Opens a new speaker view window.
+	 */
+	function openSpeakerWindow() {
+
+		// If a window is already open, focus it
+		if( speakerWindow && !speakerWindow.closed ) {
+			speakerWindow.focus();
+		}
+		else {
+			speakerWindow = window.open( 'about:blank', 'reveal.js - Notes', 'width=1100,height=700' );
+			speakerWindow.marked = marked;
+			speakerWindow.document.write( speakerViewHTML );
+
+			if( !speakerWindow ) {
+				alert( 'Speaker view popup failed to open. Please make sure popups are allowed and reopen the speaker view.' );
+				return;
+			}
 
-	function openNotes() {
+			connect();
+		}
 
-        if (popup && !popup.closed) {
-            popup.focus();
-            return;
-        }
+	}
 
-		popup = window.open( 'about:blank', 'reveal.js - Notes', 'width=1100,height=700' );
-		popup.marked = marked;
-		popup.document.write( speakerViewHTML );
+	/**
+	 * Reconnect with an existing speaker view window.
+	 */
+	function reconnectSpeakerWindow( reconnectWindow ) {
 
-		if( !popup ) {
-			alert( 'Speaker view popup failed to open. Please make sure popups are allowed and reopen the speaker view.' );
-			return;
+		if( speakerWindow && !speakerWindow.closed ) {
+			speakerWindow.focus();
 		}
-
-		/**
-		 * Connect to the notes window through a postmessage handshake.
-		 * Using postmessage enables us to work in situations where the
-		 * origins differ, such as a presentation being opened from the
-		 * file system.
-		 */
-		function connect() {
-			// Keep trying to connect until we get a 'connected' message back
-			let connectInterval = setInterval( function() {
-				popup.postMessage( JSON.stringify( {
-					namespace: 'reveal-notes',
-					type: 'connect',
-					url: window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search,
-					state: deck.getState()
-				} ), '*' );
-			}, 500 );
-
-			window.addEventListener( 'message', function( event ) {
-				let data = JSON.parse( event.data );
-				if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) {
-					clearInterval( connectInterval );
-					onConnected();
-				}
-				if( data && data.namespace === 'reveal-notes' && data.type === 'call' ) {
-					callRevealApi( data.methodName, data.arguments, data.callId );
-				}
-			} );
+		else {
+			speakerWindow = reconnectWindow;
+			window.addEventListener( 'message', onPostMessage );
+			onConnected();
 		}
 
-		/**
-		 * Calls the specified Reveal.js method with the provided argument
-		 * and then pushes the result to the notes frame.
-		 */
-		function callRevealApi( methodName, methodArguments, callId ) {
+	}
 
-			let result = deck[methodName].apply( deck, methodArguments );
-			popup.postMessage( JSON.stringify( {
+	/**
+		* Connect to the notes window through a postmessage handshake.
+		* Using postmessage enables us to work in situations where the
+		* origins differ, such as a presentation being opened from the
+		* file system.
+		*/
+	function connect() {
+
+		// Keep trying to connect until we get a 'connected' message back
+		connectInterval = setInterval( function() {
+			speakerWindow.postMessage( JSON.stringify( {
 				namespace: 'reveal-notes',
-				type: 'return',
-				result: result,
-				callId: callId
+				type: 'connect',
+				url: window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search,
+				state: deck.getState()
 			} ), '*' );
+		}, 500 );
 
-		}
+		window.addEventListener( 'message', onPostMessage );
 
-		/**
-		 * Posts the current slide data to the notes window
-		 */
-		function post( event ) {
+	}
 
-			let slideElement = deck.getCurrentSlide(),
-				notesElement = slideElement.querySelector( 'aside.notes' ),
-				fragmentElement = slideElement.querySelector( '.current-fragment' );
+	/**
+	 * Calls the specified Reveal.js method with the provided argument
+	 * and then pushes the result to the notes frame.
+	 */
+	function callRevealApi( methodName, methodArguments, callId ) {
 
-			let messageData = {
-				namespace: 'reveal-notes',
-				type: 'state',
-				notes: '',
-				markdown: false,
-				whitespace: 'normal',
-				state: deck.getState()
-			};
+		let result = deck[methodName].apply( deck, methodArguments );
+		speakerWindow.postMessage( JSON.stringify( {
+			namespace: 'reveal-notes',
+			type: 'return',
+			result,
+			callId
+		} ), '*' );
 
-			// Look for notes defined in a slide attribute
-			if( slideElement.hasAttribute( 'data-notes' ) ) {
-				messageData.notes = slideElement.getAttribute( 'data-notes' );
-				messageData.whitespace = 'pre-wrap';
-			}
+	}
 
-			// Look for notes defined in a fragment
-			if( fragmentElement ) {
-				let fragmentNotes = fragmentElement.querySelector( 'aside.notes' );
-				if( fragmentNotes ) {
-					notesElement = fragmentNotes;
-				}
-				else if( fragmentElement.hasAttribute( 'data-notes' ) ) {
-					messageData.notes = fragmentElement.getAttribute( 'data-notes' );
-					messageData.whitespace = 'pre-wrap';
+	/**
+	 * Posts the current slide data to the notes window.
+	 */
+	function post( event ) {
+
+		let slideElement = deck.getCurrentSlide(),
+			notesElement = slideElement.querySelector( 'aside.notes' ),
+			fragmentElement = slideElement.querySelector( '.current-fragment' );
+
+		let messageData = {
+			namespace: 'reveal-notes',
+			type: 'state',
+			notes: '',
+			markdown: false,
+			whitespace: 'normal',
+			state: deck.getState()
+		};
+
+		// Look for notes defined in a slide attribute
+		if( slideElement.hasAttribute( 'data-notes' ) ) {
+			messageData.notes = slideElement.getAttribute( 'data-notes' );
+			messageData.whitespace = 'pre-wrap';
+		}
 
-					// In case there are slide notes
-					notesElement = null;
-				}
+		// Look for notes defined in a fragment
+		if( fragmentElement ) {
+			let fragmentNotes = fragmentElement.querySelector( 'aside.notes' );
+			if( fragmentNotes ) {
+				notesElement = fragmentNotes;
 			}
+			else if( fragmentElement.hasAttribute( 'data-notes' ) ) {
+				messageData.notes = fragmentElement.getAttribute( 'data-notes' );
+				messageData.whitespace = 'pre-wrap';
 
-			// Look for notes defined in an aside element
-			if( notesElement ) {
-				messageData.notes = notesElement.innerHTML;
-				messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string';
+				// In case there are slide notes
+				notesElement = null;
 			}
+		}
 
-			popup.postMessage( JSON.stringify( messageData ), '*' );
-
+		// Look for notes defined in an aside element
+		if( notesElement ) {
+			messageData.notes = notesElement.innerHTML;
+			messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string';
 		}
 
-		/**
-		 * Called once we have established a connection to the notes
-		 * window.
-		 */
-		function onConnected() {
+		speakerWindow.postMessage( JSON.stringify( messageData ), '*' );
 
-			// Monitor events that trigger a change in state
-			deck.on( 'slidechanged', post );
-			deck.on( 'fragmentshown', post );
-			deck.on( 'fragmenthidden', post );
-			deck.on( 'overviewhidden', post );
-			deck.on( 'overviewshown', post );
-			deck.on( 'paused', post );
-			deck.on( 'resumed', post );
+	}
 
-			// Post the initial state
-			post();
+	function onPostMessage( event ) {
 
+		let data = JSON.parse( event.data );
+		if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) {
+			clearInterval( connectInterval );
+			onConnected();
+		}
+		else if( data && data.namespace === 'reveal-notes' && data.type === 'call' ) {
+			callRevealApi( data.methodName, data.arguments, data.callId );
 		}
 
-		connect();
+	}
+
+	/**
+	 * Called once we have established a connection to the notes
+	 * window.
+	 */
+	function onConnected() {
+
+		// Monitor events that trigger a change in state
+		deck.on( 'slidechanged', post );
+		deck.on( 'fragmentshown', post );
+		deck.on( 'fragmenthidden', post );
+		deck.on( 'overviewhidden', post );
+		deck.on( 'overviewshown', post );
+		deck.on( 'paused', post );
+		deck.on( 'resumed', post );
+
+		// Post the initial state
+		post();
 
 	}
 
@@ -164,19 +190,33 @@ const Plugin = () => {
 
 				// If the there's a 'notes' query set, open directly
 				if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) {
-					openNotes();
+					openSpeakerWindow();
+				}
+				else {
+					// Keep listening for speaker view hearbeats. If we receive a
+					// heartbeat from an orphaned window, reconnect it. This ensures
+					// that we remain connected to the notes even if the presentation
+					// is reloaded.
+					window.addEventListener( 'message', event => {
+						if( !speakerWindow ) {
+							let data = JSON.parse( event.data );
+							if( data && data.namespace === 'reveal-notes' && data.type === 'heartbeat' ) {
+								reconnectSpeakerWindow( event.source );
+							}
+						}
+					});
 				}
 
 				// Open the notes when the 's' key is hit
 				deck.addKeyBinding({keyCode: 83, key: 'S', description: 'Speaker notes view'}, function() {
-					openNotes();
+					openSpeakerWindow();
 				} );
 
 			}
 
 		},
 
-		open: openNotes
+		open: openSpeakerWindow
 	};
 
 };

+ 13 - 0
plugin/notes/speaker-view.html

@@ -435,6 +435,7 @@
 						setupKeyboard();
 						setupNotes();
 						setupTimer();
+						setupHeartbeat();
 					}
 
 				}
@@ -536,6 +537,18 @@
 
 				}
 
+				/**
+				 * We send out a heartbeat at all times to ensure we can
+				 * reconnect with the main presentation window after reloads.
+				 */
+				function setupHeartbeat() {
+
+					setInterval( () => {
+						window.opener.postMessage( JSON.stringify({ namespace: 'reveal-notes', type: 'heartbeat'} ), '*' );
+					}, 1000 );
+
+				}
+
 				function getTimings( callback ) {
 
 					callRevealApi( 'getSlidesAttributes', [], function ( slideAttributes ) {

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott