BeamerReveal

 view release on metacpan or  search on metacpan

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

	  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 {
          display: flex !important;
          flex-direction: column !important;
 	  height: 100% !important;
	  padding: 10px 16px;
      }
      
      .speaker-controls-notes .value {
	  flex: 1 !important;
	  display: flex !important;
 	  align-items: center !important;
 	  justify-content: center !important;
 	  overflow: hidden !important;
	  margin-top: 5px;
	  line-height: 1.4;
	  font-size: 1.2em;
      }
      
      .speaker-controls-notes .value img {
 	  max-width: 100% !important;
 	  max-height: 100% !important;
 	  object-fit: contain !important;
      }
      
      /* Layout selector */
      #speaker-layout {
	  position: absolute;
	  top: 10px;
	  right: 10px;
	  color: #222;
	  z-index: 10;
      }

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


      /* 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.';

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

			  callback(null);
			  return;
		      }
		      // Setting totalTime overrides defaultTiming
		      if (totalTime) {
			  defaultTiming = 0;
		      }
		      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);
		      }
		      if ( totalTime ) {
			  // After we've allocated time to individual slides, we summarize it and
			  // subtract it from the total time
			  var remainingTime = totalTime - timings.reduce( function(a, b) { return a + b; }, 0 );
			  // The remaining time is divided by the number of slides that have 0 seconds
			  // allocated at the moment, giving the average time-per-slide on the remaining slides
			  var remainingSlides = (timings.filter( function(x) { return x == 0 }) ).length
			  var timePerSlide = Math.round( remainingTime / remainingSlides, 0 )
			  // And now we replace every zero-value timing with that average
			  timings = timings.map( function(x) { return (x==0 ? timePerSlide : x) } );
		      }
		      var slidesUnderMinimum = timings.filter( function(x) { return (x < minTimePerSlide) } ).length
		      if ( slidesUnderMinimum ) {
			  message = "The pacing time for " + slidesUnderMinimum + " slide(s) is under the configured minimum of " + minTimePerSlide + " seconds. Check the data-timing attribute on individual slides, or consider increasing the totalTime or minimumTimePerSl...
			  alert(message);
		      }
		      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) {



( run in 0.514 second using v1.01-cache-2.11-cpan-39bf76dae61 )