Newer
Older

Hakim El Hattab
committed
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>reveal.js - Slide Notes</title>
<style>
body {
font-family: Helvetica;
}
#speaker-controls {
padding: 6px;
box-sizing: border-box;
-moz-box-sizing: border-box;

Hakim El Hattab
committed
}
width: 100%;
height: 100%;
border: 1px solid #ddd;

Hakim El Hattab
committed
}
position: absolute;
top: 10px;
left: 10px;
font-weight: bold;
font-size: 14px;
z-index: 2;
color: rgba( 255, 255, 255, 0.9 );

Hakim El Hattab
committed
}
#current-slide {
position: absolute;
width: 65%;
height: 100%;
top: 0;
left: 0;
padding-right: 0;

Hakim El Hattab
committed
}
position: absolute;
width: 35%;
height: 40%;
right: 0;
top: 0;

Hakim El Hattab
committed
}

Hakim El Hattab
committed
position: absolute;
top: 40%;
right: 0;
width: 35%;
height: 60%;
}
.speaker-controls-time.hidden,
.speaker-controls-notes.hidden {
display: none;
}
.speaker-controls-time .label,
.speaker-controls-notes .label {
text-transform: uppercase;
font-weight: normal;
font-size: 0.66em;
color: #666;
margin: 0;
}
.speaker-controls-time {
border-bottom: 1px solid rgba( 200, 200, 200, 0.5 );
margin-bottom: 10px;
padding: 10px 16px;
padding-bottom: 20px;
cursor: pointer;
}
.speaker-controls-time .reset-button {
opacity: 0;
float: right;
color: #666;
text-decoration: none;
}
.speaker-controls-time:hover .reset-button {
opacity: 1;
}
.speaker-controls-time .timer,
.speaker-controls-time .clock {
width: 50%;
font-size: 1.9em;
}
.speaker-controls-time .timer {
float: left;
}

Hakim El Hattab
committed
.speaker-controls-time .clock {
float: right;
text-align: right;
}
.speaker-controls-time span.mute {
color: #bbb;
}
.speaker-controls-notes {
padding: 10px 16px;
}
.speaker-controls-notes .value {
margin-top: 5px;
line-height: 1.4;
font-size: 1.2em;
}
.clear {
clear: both;

Hakim El Hattab
committed
@media screen and (max-width: 1080px) {
#speaker-controls {
font-size: 16px;
}

Hakim El Hattab
committed
@media screen and (max-width: 900px) {
#speaker-controls {
font-size: 14px;
}

Hakim El Hattab
committed
@media screen and (max-width: 800px) {
#speaker-controls {
font-size: 12px;
}

Hakim El Hattab
committed
</style>
</head>
<body>
<div id="current-slide"></div>
<div id="upcoming-slide"><span class="label">UPCOMING:</span></div>
<div id="speaker-controls">
<div class="speaker-controls-time">
<h4 class="label">Time <span class="reset-button">Click to Reset</span></h4>
<div class="clock">
<span class="clock-value">0:00 AM</span>
</div>
<div class="timer">
<span class="hours-value">00</span><span class="minutes-value">:00</span><span class="seconds-value">:00</span>
</div>
<div class="clear"></div>
<div class="speaker-controls-notes hidden">
<h4 class="label">Notes</h4>
<div class="value"></div>

Hakim El Hattab
committed
<script src="../../plugin/markdown/marked.js"></script>

Hakim El Hattab
committed
<script>

Hakim El Hattab
committed

Hakim El Hattab
committed
(function() {
var notes,

Hakim El Hattab
committed
currentState,
currentSlide,

Hakim El Hattab
committed
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
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 );
setupKeyboard();

Hakim El Hattab
committed
setupTimer();
}

Hakim El Hattab
committed

Hakim El Hattab
committed
}

Hakim El Hattab
committed
/**
* Called when the main window sends an updated state.
*/
function handleStateMessage( data ) {

Hakim El Hattab
committed
// Store the most recently set state to avoid circular loops
// applying the same state
currentState = JSON.stringify( data.state );

Hakim El Hattab
committed
// No need for updating the notes in case of fragment changes
if ( data.notes ) {
notes.classList.remove( 'hidden' );

Hakim El Hattab
committed
if( data.markdown ) {
notesValue.innerHTML = marked( data.notes );

Hakim El Hattab
committed
}
else {
notesValue.innerHTML = data.notes;

Hakim El Hattab
committed
}

Hakim El Hattab
committed
}
else {
notes.classList.add( 'hidden' );
}

Hakim El Hattab
committed
// Update the note slides
currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ] }), '*' );
upcomingSlide.contentWindow.postMessage( JSON.stringify({ method: 'next' }), '*' );

Hakim El Hattab
committed

Hakim El Hattab
committed
}
// Limit to max one state update per X ms
handleStateMessage = debounce( handleStateMessage, 200 );
/**
* Forward keyboard events to the current slide window.
* This enables keyboard events to work even if focus
* isn't set on the current slide iframe.
*/
function setupKeyboard() {
document.addEventListener( 'keydown', function( event ) {
currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'triggerKey', args: [ event.keyCode ] }), '*' );
} );
}

Hakim El Hattab
committed
/**
* Creates the preview iframes.
*/
function setupIframes( data ) {

Hakim El Hattab
committed
var params = [
'receiver',
'progress=false',
'backgroundTransition=none'
].join( '&' );

Hakim El Hattab
committed
var hash = '#/' + data.state.indexh + '/' + data.state.indexv;
var currentURL = data.url + '?' + params + '&postMessageEvents=true' + hash;
var upcomingURL = data.url + '?' + params + '&controls=false' + hash;

Hakim El Hattab
committed
currentSlide = document.createElement( 'iframe' );
currentSlide.setAttribute( 'width', 1280 );
currentSlide.setAttribute( 'height', 1024 );
currentSlide.setAttribute( 'src', currentURL );
document.querySelector( '#current-slide' ).appendChild( currentSlide );

Hakim El Hattab
committed
upcomingSlide = document.createElement( 'iframe' );
upcomingSlide.setAttribute( 'width', 640 );
upcomingSlide.setAttribute( 'height', 512 );
upcomingSlide.setAttribute( 'src', upcomingURL );
document.querySelector( '#upcoming-slide' ).appendChild( upcomingSlide );
}
/**
* Setup the notes UI.
*/
function setupNotes() {
notes = document.querySelector( '.speaker-controls-notes' );
notesValue = document.querySelector( '.speaker-controls-notes .value' );

Hakim El Hattab
committed
}

Hakim El Hattab
committed
/**
* Create the timer and clock and start updating them
* at an interval.
*/
function setupTimer() {

Hakim El Hattab
committed
var start = new Date(),
timeEl = document.querySelector( '.speaker-controls-time' ),
clockEl = timeEl.querySelector( '.clock-value' ),
hoursEl = timeEl.querySelector( '.hours-value' ),
minutesEl = timeEl.querySelector( '.minutes-value' ),
secondsEl = timeEl.querySelector( '.seconds-value' );

Hakim El Hattab
committed
function _updateTimer() {

Hakim El Hattab
committed
var diff, hours, minutes, seconds,
now = new Date();
diff = now.getTime() - start.getTime();

Thomas Rosenau
committed
hours = Math.floor( diff / ( 1000 * 60 * 60 ) );
minutes = Math.floor( ( diff / ( 1000 * 60 ) ) % 60 );
seconds = Math.floor( ( diff / 1000 ) % 60 );

Hakim El Hattab
committed
clockEl.innerHTML = now.toLocaleTimeString( 'en-US', { hour12: true, hour: '2-digit', minute:'2-digit' } );

Hakim El Hattab
committed
hoursEl.innerHTML = zeroPadInteger( hours );

Hakim El Hattab
committed
hoursEl.className = hours > 0 ? '' : 'mute';
minutesEl.innerHTML = ':' + zeroPadInteger( minutes );
minutesEl.className = minutes > 0 ? '' : 'mute';
secondsEl.innerHTML = ':' + zeroPadInteger( seconds );

Hakim El Hattab
committed
}
// Update once directly
_updateTimer();
// Then update every second
setInterval( _updateTimer, 1000 );
timeEl.addEventListener( 'click', function() {
start = new Date();
_updateTimer();
return false;
} );
}

Hakim El Hattab
committed
function zeroPadInteger( num ) {

Hakim El Hattab
committed

Hakim El Hattab
committed
var str = '00' + parseInt( num );
return str.substring( str.length - 2 );

Hakim El Hattab
committed
}

Hakim El Hattab
committed
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
/**
* Limits the frequency at which a function can be called.
*/
function debounce( fn, ms ) {
var lastTime = 0,
timeout;
return function() {
var args = arguments;
var context = this;
clearTimeout( timeout );
var timeSinceLastCall = Date.now() - lastTime;
if( timeSinceLastCall > ms ) {
fn.apply( context, args );
lastTime = Date.now();
}
else {
timeout = setTimeout( function() {
fn.apply( context, args );
lastTime = Date.now();
}, ms - timeSinceLastCall );
}
}
}

Hakim El Hattab
committed
})();

Hakim El Hattab
committed

Hakim El Hattab
committed
</script>
</body>
</html>