diff options
Diffstat (limited to 'presentation/plugin/notes/notes.html')
-rw-r--r-- | presentation/plugin/notes/notes.html | 834 |
1 files changed, 0 insertions, 834 deletions
diff --git a/presentation/plugin/notes/notes.html b/presentation/plugin/notes/notes.html deleted file mode 100644 index 9e0b230..0000000 --- a/presentation/plugin/notes/notes.html +++ /dev/null @@ -1,834 +0,0 @@ -<!doctype html> -<html lang="en"> - <head> - <meta charset="utf-8"> - - <title>reveal.js - Slide Notes</title> - - <style> - body { - font-family: Helvetica; - font-size: 18px; - } - - #current-slide, - #upcoming-slide, - #speaker-controls { - padding: 6px; - box-sizing: border-box; - -moz-box-sizing: border-box; - } - - #current-slide iframe, - #upcoming-slide iframe { - width: 100%; - height: 100%; - border: 1px solid #ddd; - } - - #current-slide .label, - #upcoming-slide .label { - position: absolute; - top: 10px; - left: 10px; - z-index: 2; - } - - #connection-status { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 20; - padding: 30% 20% 20% 20%; - font-size: 18px; - color: #222; - background: #fff; - text-align: center; - box-sizing: border-box; - line-height: 1.4; - } - - .overlay-element { - height: 34px; - line-height: 34px; - padding: 0 10px; - text-shadow: none; - background: rgba( 220, 220, 220, 0.8 ); - color: #222; - font-size: 14px; - } - - .overlay-element.interactive:hover { - background: rgba( 220, 220, 220, 1 ); - } - - #current-slide { - position: absolute; - width: 60%; - height: 100%; - top: 0; - left: 0; - padding-right: 0; - } - - #upcoming-slide { - position: absolute; - width: 40%; - height: 40%; - right: 0; - top: 0; - } - - /* Speaker controls */ - #speaker-controls { - position: absolute; - top: 40%; - right: 0; - width: 40%; - height: 60%; - overflow: auto; - font-size: 18px; - } - - .speaker-controls-time.hidden, - .speaker-controls-notes.hidden { - display: none; - } - - .speaker-controls-time .label, - .speaker-controls-pace .label, - .speaker-controls-notes .label { - text-transform: uppercase; - font-weight: normal; - font-size: 0.66em; - color: #666; - margin: 0; - } - - .speaker-controls-time, .speaker-controls-pace { - 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%; - } - - .speaker-controls-time .timer, - .speaker-controls-time .clock, - .speaker-controls-time .pacing .hours-value, - .speaker-controls-time .pacing .minutes-value, - .speaker-controls-time .pacing .seconds-value { - font-size: 1.9em; - } - - .speaker-controls-time .timer { - float: left; - } - - .speaker-controls-time .clock { - float: right; - text-align: right; - } - - .speaker-controls-time span.mute { - opacity: 0.3; - } - - .speaker-controls-time .pacing-title { - margin-top: 5px; - } - - .speaker-controls-time .pacing.ahead { - color: blue; - } - - .speaker-controls-time .pacing.on-track { - color: green; - } - - .speaker-controls-time .pacing.behind { - color: red; - } - - .speaker-controls-notes { - padding: 10px 16px; - } - - .speaker-controls-notes .value { - margin-top: 5px; - line-height: 1.4; - font-size: 1.2em; - } - - /* Layout selector */ - #speaker-layout { - position: absolute; - top: 10px; - right: 10px; - color: #222; - z-index: 10; - } - #speaker-layout select { - position: absolute; - width: 100%; - height: 100%; - top: 0; - left: 0; - border: 0; - box-shadow: 0; - cursor: pointer; - opacity: 0; - - font-size: 1em; - background-color: transparent; - - -moz-appearance: none; - -webkit-appearance: none; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - } - - #speaker-layout select:focus { - outline: none; - box-shadow: none; - } - - .clear { - clear: both; - } - - /* Speaker layout: Wide */ - body[data-speaker-layout="wide"] #current-slide, - body[data-speaker-layout="wide"] #upcoming-slide { - width: 50%; - height: 45%; - padding: 6px; - } - - body[data-speaker-layout="wide"] #current-slide { - top: 0; - left: 0; - } - - body[data-speaker-layout="wide"] #upcoming-slide { - top: 0; - left: 50%; - } - - body[data-speaker-layout="wide"] #speaker-controls { - top: 45%; - left: 0; - width: 100%; - height: 50%; - font-size: 1.25em; - } - - /* Speaker layout: Tall */ - body[data-speaker-layout="tall"] #current-slide, - body[data-speaker-layout="tall"] #upcoming-slide { - width: 45%; - height: 50%; - padding: 6px; - } - - body[data-speaker-layout="tall"] #current-slide { - top: 0; - left: 0; - } - - body[data-speaker-layout="tall"] #upcoming-slide { - top: 50%; - left: 0; - } - - body[data-speaker-layout="tall"] #speaker-controls { - padding-top: 40px; - top: 0; - left: 45%; - width: 55%; - height: 100%; - font-size: 1.25em; - } - - /* Speaker layout: Notes only */ - body[data-speaker-layout="notes-only"] #current-slide, - body[data-speaker-layout="notes-only"] #upcoming-slide { - display: none; - } - - body[data-speaker-layout="notes-only"] #speaker-controls { - padding-top: 40px; - top: 0; - left: 0; - width: 100%; - height: 100%; - font-size: 1.25em; - } - - @media screen and (max-width: 1080px) { - body[data-speaker-layout="default"] #speaker-controls { - font-size: 16px; - } - } - - @media screen and (max-width: 900px) { - body[data-speaker-layout="default"] #speaker-controls { - font-size: 14px; - } - } - - @media screen and (max-width: 800px) { - body[data-speaker-layout="default"] #speaker-controls { - font-size: 12px; - } - } - - </style> - </head> - - <body> - - <div id="connection-status">Loading speaker view...</div> - - <div id="current-slide"></div> - <div id="upcoming-slide"><span class="overlay-element 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> - - <h4 class="label pacing-title" style="display: none">Pacing – Time to finish current slide</h4> - <div class="pacing" style="display: none"> - <span class="hours-value">00</span><span class="minutes-value">:00</span><span class="seconds-value">:00</span> - </div> - </div> - - <div class="speaker-controls-notes hidden"> - <h4 class="label">Notes</h4> - <div class="value"></div> - </div> - </div> - <div id="speaker-layout" class="overlay-element interactive"> - <span class="speaker-layout-label"></span> - <select class="speaker-layout-dropdown"></select> - </div> - - <script src="../../plugin/markdown/marked.js"></script> - <script> - - (function() { - - var notes, - notesValue, - currentState, - currentSlide, - upcomingSlide, - layoutLabel, - layoutDropdown, - pendingCalls = {}, - lastRevealApiCallId = 0, - connected = false; - - var SPEAKER_LAYOUTS = { - 'default': 'Default', - 'wide': 'Wide', - 'tall': 'Tall', - 'notes-only': 'Notes only' - }; - - setupLayout(); - - var connectionStatus = document.querySelector( '#connection-status' ); - var connectionTimeout = setTimeout( function() { - connectionStatus.innerHTML = 'Error connecting to main window.<br>Please try closing and reopening the speaker view.'; - }, 5000 ); - - window.addEventListener( 'message', function( event ) { - - clearTimeout( connectionTimeout ); - connectionStatus.style.display = 'none'; - - var data = JSON.parse( event.data ); - - // The overview mode is only useful to the reveal.js instance - // where navigation occurs so we don't sync it - if( data.state ) delete data.state.overview; - - // 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 ); - } - else if( data.type === 'return' ) { - pendingCalls[data.callId](data.result); - delete pendingCalls[data.callId]; - } - } - // 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|paused|resumed/.test( data.eventName ) && currentState !== JSON.stringify( data.state ) ) { - - window.opener.postMessage( JSON.stringify({ method: 'setState', args: [ data.state ]} ), '*' ); - - } - } - - } ); - - /** - * Asynchronously calls the Reveal.js API of the main frame. - */ - function callRevealApi( methodName, methodArguments, callback ) { - - var callId = ++lastRevealApiCallId; - pendingCalls[callId] = callback; - window.opener.postMessage( JSON.stringify( { - namespace: 'reveal-notes', - type: 'call', - callId: callId, - methodName: methodName, - arguments: methodArguments - } ), '*' ); - - } - - /** - * Called when the main window is trying to establish a - * connection. - */ - function handleConnectMessage( data ) { - - if( connected === false ) { - connected = true; - - setupIframes( data ); - setupKeyboard(); - setupNotes(); - 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 - if ( data.notes ) { - notes.classList.remove( 'hidden' ); - notesValue.style.whiteSpace = data.whitespace; - if( data.markdown ) { - notesValue.innerHTML = marked( data.notes ); - } - else { - notesValue.innerHTML = data.notes; - } - } - else { - notes.classList.add( 'hidden' ); - } - - // 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' }), '*' ); - - } - - // 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. - * - * Block F5 default handling, it reloads and disconnects - * the speaker notes window. - */ - function setupKeyboard() { - - document.addEventListener( 'keydown', function( event ) { - if( event.keyCode === 116 || ( event.metaKey && event.keyCode === 82 ) ) { - event.preventDefault(); - return false; - } - currentSlide.contentWindow.postMessage( JSON.stringify({ method: 'triggerKey', args: [ event.keyCode ] }), '*' ); - } ); - - } - - /** - * Creates the preview iframes. - */ - function setupIframes( data ) { - - var params = [ - 'receiver', - 'progress=false', - 'history=false', - 'transition=none', - 'autoSlide=0', - 'backgroundTransition=none' - ].join( '&' ); - - var urlSeparator = /\?/.test(data.url) ? '&' : '?'; - var hash = '#/' + data.state.indexh + '/' + data.state.indexv; - var currentURL = data.url + urlSeparator + params + '&postMessageEvents=true' + hash; - var upcomingURL = data.url + urlSeparator + params + '&controls=false' + hash; - - currentSlide = document.createElement( 'iframe' ); - currentSlide.setAttribute( 'width', 1280 ); - currentSlide.setAttribute( 'height', 1024 ); - currentSlide.setAttribute( 'src', currentURL ); - document.querySelector( '#current-slide' ).appendChild( currentSlide ); - - 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' ); - - } - - function getTimings( callback ) { - - callRevealApi( 'getSlidesAttributes', [], function ( slideAttributes ) { - callRevealApi( 'getConfig', [], function ( config ) { - var defaultTiming = config.defaultTiming; - if (defaultTiming == null) { - callback(null); - return; - } - - var timings = []; - for ( var i in slideAttributes ) { - var slide = slideAttributes[ i ]; - var timing = defaultTiming; - if( slide.hasOwnProperty( 'data-timing' )) { - var t = slide[ 'data-timing' ]; - timing = parseInt(t); - if( isNaN(timing) ) { - console.warn("Could not parse timing '" + t + "' of slide " + i + "; using default of " + defaultTiming); - timing = defaultTiming; - } - } - timings.push(timing); - } - - callback( timings ); - } ); - } ); - - } - - /** - * Return the number of seconds allocated for presenting - * all slides up to and including this one. - */ - function getTimeAllocated( timings, callback ) { - - callRevealApi( 'getSlidePastCount', [], function ( currentSlide ) { - var allocated = 0; - for (var i in timings.slice(0, currentSlide + 1)) { - allocated += timings[i]; - } - callback( allocated ); - } ); - - } - - /** - * Create the timer and clock and start updating them - * at an interval. - */ - function setupTimer() { - - 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' ), - pacingTitleEl = timeEl.querySelector( '.pacing-title' ), - pacingEl = timeEl.querySelector( '.pacing' ), - pacingHoursEl = pacingEl.querySelector( '.hours-value' ), - pacingMinutesEl = pacingEl.querySelector( '.minutes-value' ), - pacingSecondsEl = pacingEl.querySelector( '.seconds-value' ); - - var timings = null; - getTimings( function ( _timings ) { - - timings = _timings; - if (_timings !== null) { - pacingTitleEl.style.removeProperty('display'); - pacingEl.style.removeProperty('display'); - } - - // Update once directly - _updateTimer(); - - // Then update every second - setInterval( _updateTimer, 1000 ); - - } ); - - - function _resetTimer() { - - if (timings == null) { - start = new Date(); - _updateTimer(); - } - else { - // Reset timer to beginning of current slide - getTimeAllocated( timings, function ( slideEndTimingSeconds ) { - var slideEndTiming = slideEndTimingSeconds * 1000; - callRevealApi( 'getSlidePastCount', [], function ( currentSlide ) { - var currentSlideTiming = timings[currentSlide] * 1000; - var previousSlidesTiming = slideEndTiming - currentSlideTiming; - var now = new Date(); - start = new Date(now.getTime() - previousSlidesTiming); - _updateTimer(); - } ); - } ); - } - - } - - timeEl.addEventListener( 'click', function() { - _resetTimer(); - return false; - } ); - - function _displayTime( hrEl, minEl, secEl, time) { - - var sign = Math.sign(time) == -1 ? "-" : ""; - time = Math.abs(Math.round(time / 1000)); - var seconds = time % 60; - var minutes = Math.floor( time / 60 ) % 60 ; - var hours = Math.floor( time / ( 60 * 60 )) ; - hrEl.innerHTML = sign + zeroPadInteger( hours ); - if (hours == 0) { - hrEl.classList.add( 'mute' ); - } - else { - hrEl.classList.remove( 'mute' ); - } - minEl.innerHTML = ':' + zeroPadInteger( minutes ); - if (hours == 0 && minutes == 0) { - minEl.classList.add( 'mute' ); - } - else { - minEl.classList.remove( 'mute' ); - } - secEl.innerHTML = ':' + zeroPadInteger( seconds ); - } - - function _updateTimer() { - - var diff, hours, minutes, seconds, - now = new Date(); - - diff = now.getTime() - start.getTime(); - - clockEl.innerHTML = now.toLocaleTimeString( 'en-US', { hour12: true, hour: '2-digit', minute:'2-digit' } ); - _displayTime( hoursEl, minutesEl, secondsEl, diff ); - if (timings !== null) { - _updatePacing(diff); - } - - } - - function _updatePacing(diff) { - - getTimeAllocated( timings, function ( slideEndTimingSeconds ) { - var slideEndTiming = slideEndTimingSeconds * 1000; - - callRevealApi( 'getSlidePastCount', [], function ( currentSlide ) { - var currentSlideTiming = timings[currentSlide] * 1000; - var timeLeftCurrentSlide = slideEndTiming - diff; - if (timeLeftCurrentSlide < 0) { - pacingEl.className = 'pacing behind'; - } - else if (timeLeftCurrentSlide < currentSlideTiming) { - pacingEl.className = 'pacing on-track'; - } - else { - pacingEl.className = 'pacing ahead'; - } - _displayTime( pacingHoursEl, pacingMinutesEl, pacingSecondsEl, timeLeftCurrentSlide ); - } ); - } ); - } - - } - - /** - * Sets up the speaker view layout and layout selector. - */ - function setupLayout() { - - layoutDropdown = document.querySelector( '.speaker-layout-dropdown' ); - layoutLabel = document.querySelector( '.speaker-layout-label' ); - - // Render the list of available layouts - for( var id in SPEAKER_LAYOUTS ) { - var option = document.createElement( 'option' ); - option.setAttribute( 'value', id ); - option.textContent = SPEAKER_LAYOUTS[ id ]; - layoutDropdown.appendChild( option ); - } - - // Monitor the dropdown for changes - layoutDropdown.addEventListener( 'change', function( event ) { - - setLayout( layoutDropdown.value ); - - }, false ); - - // Restore any currently persisted layout - setLayout( getLayout() ); - - } - - /** - * Sets a new speaker view layout. The layout is persisted - * in local storage. - */ - function setLayout( value ) { - - var title = SPEAKER_LAYOUTS[ value ]; - - layoutLabel.innerHTML = 'Layout' + ( title ? ( ': ' + title ) : '' ); - layoutDropdown.value = value; - - document.body.setAttribute( 'data-speaker-layout', value ); - - // Persist locally - if( supportsLocalStorage() ) { - window.localStorage.setItem( 'reveal-speaker-layout', value ); - } - - } - - /** - * Returns the ID of the most recently set speaker layout - * or our default layout if none has been set. - */ - function getLayout() { - - if( supportsLocalStorage() ) { - var layout = window.localStorage.getItem( 'reveal-speaker-layout' ); - if( layout ) { - return layout; - } - } - - // Default to the first record in the layouts hash - for( var id in SPEAKER_LAYOUTS ) { - return id; - } - - } - - function supportsLocalStorage() { - - try { - localStorage.setItem('test', 'test'); - localStorage.removeItem('test'); - return true; - } - catch( e ) { - return false; - } - - } - - function zeroPadInteger( num ) { - - var str = '00' + parseInt( num ); - return str.substring( str.length - 2 ); - - } - - /** - * 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 ); - } - - } - - } - - })(); - - </script> - </body> -</html> |