BeamerReveal

 view release on metacpan or  search on metacpan

beamer-reveal-example_files/libs/revealjs/plugin/notes/speaker-view.html  view on Meta::CPAN

<!--
    NOTE: You need to build the notes plugin after making changes to this file.
  -->
<html lang="en">
  <head>
    <meta charset="utf-8">
    
    <title>reveal.js - Speaker View</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;
      }

beamer-reveal-example_files/libs/revealjs/plugin/notes/speaker-view.html  view on Meta::CPAN

      }

      .clear {
	  clear: both;
      }

      /* Speaker layout: Wide - removed by DMW */

      /* 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;
      }

      /* Speaker layout: Default - removed by DMW */

    </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>

      (function() {

	  var notes,
	      notesValue,
	      currentState,
	      currentSlide,
	      upcomingSlide,
	      layoutLabel,
	      layoutDropdown,
	      pendingCalls = {},
	      lastRevealApiCallId = 0,
	      connected = false

	  var connectionStatus = document.querySelector( '#connection-status' );

	  var SPEAKER_LAYOUTS = {
	      /*
		'default': 'Default',
		'wide': 'Wide',
		'tall': 'Tall',
		'notes-only': 'Notes only' */
	      'tall': 'Slides & Notes',
	      'notes-only': 'Notes only'
	  };

	  setupLayout();

	  let openerOrigin;

	  try {
	      openerOrigin = window.opener.location.origin;
	  }
	  catch ( error ) { console.warn( error ) }

	  // In order to prevent XSS, the speaker view will only run if its
	  // opener has the same origin as itself
	  if( window.location.origin !== openerOrigin ) {
	      connectionStatus.innerHTML = 'Cross origin error.<br>The speaker window can only be opened from the same origin.';
	      return;
	  }

	  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 ) {

	      // Validate the origin of all messages to avoid parsing messages
	      // that aren't meant for us. Ignore when running off file:// so
	      // that the speaker view continues to work without a web server.
	      if( window.location.origin !== event.origin && window.location.origin !== 'file://' ) {
		  return
	      }

	      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' ) {
		  const supportedEvents = [
		      'slidechanged',
		      'fragmentshown',
		      'fragmenthidden',
		      'paused',
		      'resumed',
		      'previewiframe',
		      'previewimage',
		      'previewvideo',
		      'closeoverlay'
		  ];

		  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( supportedEvents.includes( data.eventName ) && currentState !== JSON.stringify( data.state ) ) {
		      dispatchStateToMainWindow( data.state );
		  }
	      }

	  } );

	  /**
	   * Updates the presentation in the main window to match the state
	   * of the presentation in the notes window.
	   */
	  const dispatchStateToMainWindow = debounce(( state ) => {
	      window.opener.postMessage( JSON.stringify({ method: 'setState', args: [ state ]} ), '*' );
	  }, 500);

	  /**
	   * 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();
		  setupHeartbeat();
	      }

	  }

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



( run in 0.997 second using v1.01-cache-2.11-cpan-6aa56a78535 )