Skip to content
Snippets Groups Projects
Commit 5b18c1f3 authored by Hakim El Hattab's avatar Hakim El Hattab
Browse files

notes plugin now operates entirely through window.postMessage, adding support for file protocol

parent ce31184b
No related branches found
No related tags found
No related merge requests found
......@@ -82,6 +82,7 @@
left: 3px;
font-weight: bold;
font-size: 14px;
z-index: 2;
color: rgba( 255, 255, 255, 0.9 );
}
......@@ -138,22 +139,8 @@
<body>
<script>
function getNotesURL( controls ) {
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 id="wrap-current-slide" class="slides"></div>
<div id="wrap-next-slide" class="slides"><span>UPCOMING:</span></div>
<div class="time">
<div class="clock">
......@@ -171,37 +158,112 @@
<script src="../../plugin/markdown/marked.js"></script>
<script>
window.addEventListener( 'load', function() {
(function() {
var notes,
currentState,
currentSlide,
nextSlide,
connected = false;
window.addEventListener( 'message', function( event ) {
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();
}
if( window.opener && window.opener.location && window.opener.location.href ) {
}
var notes = document.getElementById( 'notes' ),
currentSlide = document.getElementById( 'current-slide' ),
nextSlide = document.getElementById( 'next-slide' ),
silenced = false;
/**
* Called when the main window sends an updated state.
*/
function handleStateMessage( data ) {
window.addEventListener( 'message', function( event ) {
var data = JSON.parse( event.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
if ( data.notes !== undefined) {
if( data.markdown ) {
notes.innerHTML = marked( data.notes );
}
else {
notes.innerHTML = data.notes;
}
// No need for updating the notes in case of fragment changes
if ( data.notes !== undefined) {
if( data.markdown ) {
notes.innerHTML = marked( data.notes );
}
else {
notes.innerHTML = data.notes;
}
}
// Update the note slides
currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
nextSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
nextSlide.contentWindow.postMessage( JSON.stringify({ method: 'next' }), '*' );
silenced = true;
}
// Update the note slides
currentSlide.contentWindow.Reveal.slide( data.indexh, data.indexv, data.indexf );
nextSlide.contentWindow.Reveal.slide( data.nextindexh, data.nextindexv );
/**
* Creates the preview iframes.
*/
function setupIframes( data ) {
silenced = false;
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 );
}
}, false );
/**
* Create the timer and clock and start updating them
* at an interval.
*/
function setupTimer() {
var start = new Date(),
timeEl = document.querySelector( '.time' ),
......@@ -224,43 +286,23 @@
clockEl.innerHTML = now.toLocaleTimeString();
hoursEl.innerHTML = zeroPadInteger( hours );
hoursEl.className = hours > 0 ? "" : "mute";
minutesEl.innerHTML = ":" + zeroPadInteger( minutes );
minutesEl.className = minutes > 0 ? "" : "mute";
secondsEl.innerHTML = ":" + zeroPadInteger( seconds );
hoursEl.className = hours > 0 ? '' : 'mute';
minutesEl.innerHTML = ':' + zeroPadInteger( minutes );
minutesEl.className = minutes > 0 ? '' : 'mute';
secondsEl.innerHTML = ':' + zeroPadInteger( seconds );
}, 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>';
}
function zeroPadInteger( num ) {
var str = '00' + parseInt( num );
return str.substring( str.length - 2 );
}, false );
}
function zeroPadInteger( num ) {
var str = "00" + parseInt( num );
return str.substring( str.length - 2 );
}
})();
</script>
</body>
......
/**
* Handles opening of and synchronization with the reveal.js
* 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() {
......@@ -9,41 +16,46 @@ var RevealNotes = (function() {
jsFileLocation = jsFileLocation.replace(/notes\.js(\?.*)?$/, ''); // the js folder path
var notesPopup = window.open( jsFileLocation + 'notes.html', 'reveal.js - Notes', 'width=1120,height=850' );
// Fires when slide is changed
Reveal.addEventListener( 'slidechanged', post );
// Fires when a fragment is shown
Reveal.addEventListener( 'fragmentshown', 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 hidden
Reveal.addEventListener( 'fragmenthidden', post );
window.addEventListener( 'message', function( event ) {
var data = JSON.parse( event.data );
if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) {
clearInterval( connectInterval );
onConnected();
}
} );
}
/**
* Posts the current slide data to the notes window
*/
function post() {
var slideElement = Reveal.getCurrentSlide(),
slideIndices = Reveal.getIndices(),
notesElement = slideElement.querySelector( 'aside.notes' ),
nextindexh,
nextindexv;
if( slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION' ) {
nextindexh = slideIndices.h;
nextindexv = slideIndices.v + 1;
} else {
nextindexh = slideIndices.h + 1;
nextindexv = 0;
}
notesElement = slideElement.querySelector( 'aside.notes' );
var messageData = {
notes : '',
indexh : slideIndices.h,
indexv : slideIndices.v,
indexf : slideIndices.f,
nextindexh : nextindexh,
nextindexv : nextindexv,
markdown : false
namespace: 'reveal-notes',
type: 'state',
notes: '',
markdown: false,
state: Reveal.getState()
};
// Look for notes defined in a slide attribute
......@@ -58,12 +70,30 @@ var RevealNotes = (function() {
}
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();
}, false );
}
connect();
}
// If the there's a 'notes' query set, open directly
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment