|
import speakerViewHTML from './speaker-view.html'; |
|
|
|
import { marked } from 'marked'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const Plugin = () => { |
|
|
|
let connectInterval; |
|
let speakerWindow = null; |
|
let deck; |
|
|
|
|
|
|
|
|
|
function openSpeakerWindow() { |
|
|
|
|
|
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; |
|
} |
|
|
|
connect(); |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
function reconnectSpeakerWindow( reconnectWindow ) { |
|
|
|
if( speakerWindow && !speakerWindow.closed ) { |
|
speakerWindow.focus(); |
|
} |
|
else { |
|
speakerWindow = reconnectWindow; |
|
window.addEventListener( 'message', onPostMessage ); |
|
onConnected(); |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function connect() { |
|
|
|
const presentationURL = deck.getConfig().url; |
|
|
|
const url = typeof presentationURL === 'string' ? presentationURL : |
|
window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search; |
|
|
|
|
|
connectInterval = setInterval( function() { |
|
speakerWindow.postMessage( JSON.stringify( { |
|
namespace: 'reveal-notes', |
|
type: 'connect', |
|
state: deck.getState(), |
|
url |
|
} ), '*' ); |
|
}, 500 ); |
|
|
|
window.addEventListener( 'message', onPostMessage ); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function callRevealApi( methodName, methodArguments, callId ) { |
|
|
|
let result = deck[methodName].apply( deck, methodArguments ); |
|
speakerWindow.postMessage( JSON.stringify( { |
|
namespace: 'reveal-notes', |
|
type: 'return', |
|
result, |
|
callId |
|
} ), '*' ); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
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() |
|
}; |
|
|
|
|
|
if( slideElement.hasAttribute( 'data-notes' ) ) { |
|
messageData.notes = slideElement.getAttribute( 'data-notes' ); |
|
messageData.whitespace = 'pre-wrap'; |
|
} |
|
|
|
|
|
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'; |
|
|
|
|
|
notesElement = null; |
|
} |
|
} |
|
|
|
|
|
if( notesElement ) { |
|
messageData.notes = notesElement.innerHTML; |
|
messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string'; |
|
} |
|
|
|
speakerWindow.postMessage( JSON.stringify( messageData ), '*' ); |
|
|
|
} |
|
|
|
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 ); |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function onConnected() { |
|
|
|
|
|
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(); |
|
|
|
} |
|
|
|
return { |
|
id: 'notes', |
|
|
|
init: function( reveal ) { |
|
|
|
deck = reveal; |
|
|
|
if( !/receiver/i.test( window.location.search ) ) { |
|
|
|
|
|
if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) { |
|
openSpeakerWindow(); |
|
} |
|
else { |
|
|
|
|
|
|
|
|
|
window.addEventListener( 'message', event => { |
|
|
|
if( !speakerWindow && typeof event.data === 'string' ) { |
|
let data; |
|
|
|
try { |
|
data = JSON.parse( event.data ); |
|
} |
|
catch( error ) {} |
|
|
|
if( data && data.namespace === 'reveal-notes' && data.type === 'heartbeat' ) { |
|
reconnectSpeakerWindow( event.source ); |
|
} |
|
} |
|
}); |
|
} |
|
|
|
|
|
deck.addKeyBinding({keyCode: 83, key: 'S', description: 'Speaker notes view'}, function() { |
|
openSpeakerWindow(); |
|
} ); |
|
|
|
} |
|
|
|
}, |
|
|
|
open: openSpeakerWindow |
|
}; |
|
|
|
}; |
|
|
|
export default Plugin; |
|
|