notes plugin now operates entirely through window.postMessage, adding support for file protocol
This commit is contained in:
parent
ce31184bf3
commit
5b18c1f308
2 changed files with 168 additions and 96 deletions
|
@ -82,6 +82,7 @@
|
||||||
left: 3px;
|
left: 3px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
z-index: 2;
|
||||||
color: rgba( 255, 255, 255, 0.9 );
|
color: rgba( 255, 255, 255, 0.9 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,22 +139,8 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<script>
|
<div id="wrap-current-slide" class="slides"></div>
|
||||||
function getNotesURL( controls ) {
|
<div id="wrap-next-slide" class="slides"><span>UPCOMING:</span></div>
|
||||||
return window.opener.location.protocol + '//' + window.opener.location.host + window.opener.location.pathname + '?receiver&controls='+ ( controls || 'false' ) +'&progress=false&overview=false' + window.opener.location.hash;
|
|
||||||
}
|
|
||||||
var notesCurrentSlideURL = getNotesURL( true );
|
|
||||||
var notesNextSlideURL = getNotesURL( false );
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div id="wrap-current-slide" class="slides">
|
|
||||||
<script>document.write( '<iframe width="1280" height="1024" id="current-slide" src="'+ notesCurrentSlideURL +'"></iframe>' );</script>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="wrap-next-slide" class="slides">
|
|
||||||
<script>document.write( '<iframe width="640" height="512" id="next-slide" src="'+ notesNextSlideURL +'"></iframe>' );</script>
|
|
||||||
<span>UPCOMING:</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="time">
|
<div class="time">
|
||||||
<div class="clock">
|
<div class="clock">
|
||||||
|
@ -171,18 +158,64 @@
|
||||||
<script src="../../plugin/markdown/marked.js"></script>
|
<script src="../../plugin/markdown/marked.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
window.addEventListener( 'load', function() {
|
(function() {
|
||||||
|
|
||||||
if( window.opener && window.opener.location && window.opener.location.href ) {
|
var notes,
|
||||||
|
currentState,
|
||||||
var notes = document.getElementById( 'notes' ),
|
currentSlide,
|
||||||
currentSlide = document.getElementById( 'current-slide' ),
|
nextSlide,
|
||||||
nextSlide = document.getElementById( 'next-slide' ),
|
connected = false;
|
||||||
silenced = false;
|
|
||||||
|
|
||||||
window.addEventListener( 'message', function( event ) {
|
window.addEventListener( 'message', function( event ) {
|
||||||
|
|
||||||
var data = JSON.parse( event.data );
|
var data = JSON.parse( event.data );
|
||||||
|
|
||||||
|
// Messages sent by the notes plugin inside of the main window
|
||||||
|
if( data && data.namespace === 'reveal-notes' ) {
|
||||||
|
if( data.type === 'connect' ) {
|
||||||
|
handleConnectMessage( data );
|
||||||
|
}
|
||||||
|
else if( data.type === 'state' ) {
|
||||||
|
handleStateMessage( data );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Messages sent by the reveal.js inside of the current slide preview
|
||||||
|
else if( data && data.namespace === 'reveal' ) {
|
||||||
|
if( /ready/.test( data.eventName ) ) {
|
||||||
|
// Send a message back to notify that the handshake is complete
|
||||||
|
window.opener.postMessage( JSON.stringify({ namespace: 'reveal-notes', type: 'connected'} ), '*' );
|
||||||
|
}
|
||||||
|
else if( /slidechanged|fragmentshown|fragmenthidden|overviewshown|overviewhidden|paused|resumed/.test( data.eventName ) && currentState !== JSON.stringify( data.state ) ) {
|
||||||
|
window.opener.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ]} ), '*' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the main window is trying to establish a
|
||||||
|
* connection.
|
||||||
|
*/
|
||||||
|
function handleConnectMessage( data ) {
|
||||||
|
|
||||||
|
if( connected === false ) {
|
||||||
|
connected = true;
|
||||||
|
|
||||||
|
setupIframes( data );
|
||||||
|
setupTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the main window sends an updated state.
|
||||||
|
*/
|
||||||
|
function handleStateMessage( data ) {
|
||||||
|
|
||||||
|
// Store the most recently set state to avoid circular loops
|
||||||
|
// applying the same state
|
||||||
|
currentState = JSON.stringify( data.state );
|
||||||
|
|
||||||
// No need for updating the notes in case of fragment changes
|
// No need for updating the notes in case of fragment changes
|
||||||
if ( data.notes !== undefined) {
|
if ( data.notes !== undefined) {
|
||||||
if( data.markdown ) {
|
if( data.markdown ) {
|
||||||
|
@ -193,15 +226,44 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
silenced = true;
|
|
||||||
|
|
||||||
// Update the note slides
|
// Update the note slides
|
||||||
currentSlide.contentWindow.Reveal.slide( data.indexh, data.indexv, data.indexf );
|
currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
|
||||||
nextSlide.contentWindow.Reveal.slide( data.nextindexh, data.nextindexv );
|
nextSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
|
||||||
|
nextSlide.contentWindow.postMessage( JSON.stringify({ method: 'next' }), '*' );
|
||||||
|
|
||||||
silenced = false;
|
}
|
||||||
|
|
||||||
}, false );
|
/**
|
||||||
|
* Creates the preview iframes.
|
||||||
|
*/
|
||||||
|
function setupIframes( data ) {
|
||||||
|
|
||||||
|
notes = document.getElementById( 'notes' );
|
||||||
|
|
||||||
|
var url = data.url + '?receiver&progress=false&overview=false&history=false';
|
||||||
|
var hash = '#/' + data.state.indexh + '/' + data.state.indexv;
|
||||||
|
|
||||||
|
currentSlide = document.createElement( 'iframe' );
|
||||||
|
currentSlide.setAttribute( 'id', 'current-slide' );
|
||||||
|
currentSlide.setAttribute( 'width', 1280 );
|
||||||
|
currentSlide.setAttribute( 'height', 1024 );
|
||||||
|
currentSlide.setAttribute( 'src', url + '&postMessageEvents=true' + hash );
|
||||||
|
document.querySelector( '#wrap-current-slide' ).appendChild( currentSlide );
|
||||||
|
|
||||||
|
nextSlide = document.createElement( 'iframe' );
|
||||||
|
nextSlide.setAttribute( 'id', 'next-slide' );
|
||||||
|
nextSlide.setAttribute( 'width', 640 );
|
||||||
|
nextSlide.setAttribute( 'height', 512 );
|
||||||
|
nextSlide.setAttribute( 'src', url + '&controls=false' + hash );
|
||||||
|
document.querySelector( '#wrap-next-slide' ).appendChild( nextSlide );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the timer and clock and start updating them
|
||||||
|
* at an interval.
|
||||||
|
*/
|
||||||
|
function setupTimer() {
|
||||||
|
|
||||||
var start = new Date(),
|
var start = new Date(),
|
||||||
timeEl = document.querySelector( '.time' ),
|
timeEl = document.querySelector( '.time' ),
|
||||||
|
@ -224,44 +286,24 @@
|
||||||
|
|
||||||
clockEl.innerHTML = now.toLocaleTimeString();
|
clockEl.innerHTML = now.toLocaleTimeString();
|
||||||
hoursEl.innerHTML = zeroPadInteger( hours );
|
hoursEl.innerHTML = zeroPadInteger( hours );
|
||||||
hoursEl.className = hours > 0 ? "" : "mute";
|
hoursEl.className = hours > 0 ? '' : 'mute';
|
||||||
minutesEl.innerHTML = ":" + zeroPadInteger( minutes );
|
minutesEl.innerHTML = ':' + zeroPadInteger( minutes );
|
||||||
minutesEl.className = minutes > 0 ? "" : "mute";
|
minutesEl.className = minutes > 0 ? '' : 'mute';
|
||||||
secondsEl.innerHTML = ":" + zeroPadInteger( seconds );
|
secondsEl.innerHTML = ':' + zeroPadInteger( seconds );
|
||||||
|
|
||||||
}, 1000 );
|
}, 1000 );
|
||||||
|
|
||||||
// Broadcasts the state of the notes window to synchronize
|
|
||||||
// the main window
|
|
||||||
function synchronizeMainWindow() {
|
|
||||||
|
|
||||||
if( !silenced ) {
|
|
||||||
var indices = currentSlide.contentWindow.Reveal.getIndices();
|
|
||||||
window.opener.Reveal.slide( indices.h, indices.v, indices.f );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Navigate the main window when the notes slide changes
|
|
||||||
currentSlide.contentWindow.Reveal.addEventListener( 'slidechanged', synchronizeMainWindow );
|
|
||||||
currentSlide.contentWindow.Reveal.addEventListener( 'fragmentshown', synchronizeMainWindow );
|
|
||||||
currentSlide.contentWindow.Reveal.addEventListener( 'fragmenthidden', synchronizeMainWindow );
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
document.body.innerHTML = '<p class="error">Unable to access <code>window.opener.location</code>.<br>Make sure the presentation is running on a web server.</p>';
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}, false );
|
|
||||||
|
|
||||||
function zeroPadInteger( num ) {
|
function zeroPadInteger( num ) {
|
||||||
var str = "00" + parseInt( num );
|
|
||||||
|
var str = '00' + parseInt( num );
|
||||||
return str.substring( str.length - 2 );
|
return str.substring( str.length - 2 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
/**
|
/**
|
||||||
* Handles opening of and synchronization with the reveal.js
|
* Handles opening of and synchronization with the reveal.js
|
||||||
* notes window.
|
* notes window.
|
||||||
|
*
|
||||||
|
* Handshake process:
|
||||||
|
* 1. This window posts 'connect' to notes window
|
||||||
|
* - Includes URL of presentation to show
|
||||||
|
* 2. Notes window responds with 'connected' when it is available
|
||||||
|
* 3. This window proceeds to send the current presentation state
|
||||||
|
* to the notes window
|
||||||
*/
|
*/
|
||||||
var RevealNotes = (function() {
|
var RevealNotes = (function() {
|
||||||
|
|
||||||
|
@ -9,41 +16,46 @@ var RevealNotes = (function() {
|
||||||
jsFileLocation = jsFileLocation.replace(/notes\.js(\?.*)?$/, ''); // the js folder path
|
jsFileLocation = jsFileLocation.replace(/notes\.js(\?.*)?$/, ''); // the js folder path
|
||||||
var notesPopup = window.open( jsFileLocation + 'notes.html', 'reveal.js - Notes', 'width=1120,height=850' );
|
var notesPopup = window.open( jsFileLocation + 'notes.html', 'reveal.js - Notes', 'width=1120,height=850' );
|
||||||
|
|
||||||
// Fires when slide is changed
|
/**
|
||||||
Reveal.addEventListener( 'slidechanged', post );
|
* 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
|
||||||
|
var connectInterval = setInterval( function() {
|
||||||
|
notesPopup.postMessage( JSON.stringify( {
|
||||||
|
namespace: 'reveal-notes',
|
||||||
|
type: 'connect',
|
||||||
|
url: window.location.protocol + '//' + window.location.host + window.location.pathname,
|
||||||
|
state: Reveal.getState()
|
||||||
|
} ), '*' );
|
||||||
|
}, 500 );
|
||||||
|
|
||||||
// Fires when a fragment is shown
|
window.addEventListener( 'message', function( event ) {
|
||||||
Reveal.addEventListener( 'fragmentshown', post );
|
var data = JSON.parse( event.data );
|
||||||
|
if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) {
|
||||||
// Fires when a fragment is hidden
|
clearInterval( connectInterval );
|
||||||
Reveal.addEventListener( 'fragmenthidden', post );
|
onConnected();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Posts the current slide data to the notes window
|
* Posts the current slide data to the notes window
|
||||||
*/
|
*/
|
||||||
function post() {
|
function post() {
|
||||||
var slideElement = Reveal.getCurrentSlide(),
|
|
||||||
slideIndices = Reveal.getIndices(),
|
|
||||||
notesElement = slideElement.querySelector( 'aside.notes' ),
|
|
||||||
nextindexh,
|
|
||||||
nextindexv;
|
|
||||||
|
|
||||||
if( slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION' ) {
|
var slideElement = Reveal.getCurrentSlide(),
|
||||||
nextindexh = slideIndices.h;
|
notesElement = slideElement.querySelector( 'aside.notes' );
|
||||||
nextindexv = slideIndices.v + 1;
|
|
||||||
} else {
|
|
||||||
nextindexh = slideIndices.h + 1;
|
|
||||||
nextindexv = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var messageData = {
|
var messageData = {
|
||||||
notes : '',
|
namespace: 'reveal-notes',
|
||||||
indexh : slideIndices.h,
|
type: 'state',
|
||||||
indexv : slideIndices.v,
|
notes: '',
|
||||||
indexf : slideIndices.f,
|
markdown: false,
|
||||||
nextindexh : nextindexh,
|
state: Reveal.getState()
|
||||||
nextindexv : nextindexv,
|
|
||||||
markdown : false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Look for notes defined in a slide attribute
|
// Look for notes defined in a slide attribute
|
||||||
|
@ -58,12 +70,30 @@ var RevealNotes = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
notesPopup.postMessage( JSON.stringify( messageData ), '*' );
|
notesPopup.postMessage( JSON.stringify( messageData ), '*' );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigate to the current slide when the notes are loaded
|
/**
|
||||||
notesPopup.addEventListener( 'load', function( event ) {
|
* Called once we have established a connection to the notes
|
||||||
|
* window.
|
||||||
|
*/
|
||||||
|
function onConnected() {
|
||||||
|
|
||||||
|
// Monitor events that trigger a change in state
|
||||||
|
Reveal.addEventListener( 'slidechanged', post );
|
||||||
|
Reveal.addEventListener( 'fragmentshown', post );
|
||||||
|
Reveal.addEventListener( 'fragmenthidden', post );
|
||||||
|
Reveal.addEventListener( 'overviewhidden', post );
|
||||||
|
Reveal.addEventListener( 'overviewshown', post );
|
||||||
|
Reveal.addEventListener( 'paused', post );
|
||||||
|
Reveal.addEventListener( 'resumed', post );
|
||||||
|
|
||||||
|
// Post the initial state
|
||||||
post();
|
post();
|
||||||
}, false );
|
|
||||||
|
}
|
||||||
|
|
||||||
|
connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the there's a 'notes' query set, open directly
|
// If the there's a 'notes' query set, open directly
|
||||||
|
|
Loading…
Reference in a new issue