gotools/cmd/present/static/notes.js

// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Store child window object which will display slides with notes
var notesWindow = null;

var isParentWindow = window.parent == window;

// When parent window closes, clear storage and close child window
if (isParentWindow) {
  window.onbeforeunload = function() {
    localStorage.clear();
    if (notesWindow) notesWindow.close();
  };
}

function toggleNotesWindow() {
  if (!isParentWindow) return;
  if (notesWindow) {
    notesWindow.close();
    notesWindow = null;
    return;
  }

  initNotes();
}

// Create a unique key for the local storage so we don't mix the
// destSlide of different presentations. For golang.org/issue/24688.
function destSlideKey() {
  var key = '';
  if (notesWindow) {
    var slides = notesWindow.document.getElementById('presenter-slides');
    key = slides.src.split('#')[0];
  } else {
    key = window.location.href.split('#')[0];
  }
  return 'destSlide:' + key;
}

function initNotes() {
  notesWindow = window.open('', '', 'width=1000,height=700');
  var w = notesWindow;
  var slidesUrl = window.location.href;

  // Hack to apply css. Requires existing html on notesWindow.
  w.document.write("<div style='display:none;'></div>");

  w.document.title = window.document.title;

  var slides = w.document.createElement('iframe');
  slides.id = 'presenter-slides';
  slides.src = slidesUrl;
  w.document.body.appendChild(slides);

  var curSlide = parseInt(localStorage.getItem(destSlideKey()), 10);
  var formattedNotes = '';
  var section = sections[curSlide - 1];
  // curSlide is 0 when initialized from the first page of slides.
  // Check if section is valid before retrieving Notes.
  if (section) {
    formattedNotes = formatNotes(section.Notes);
  } else if (curSlide == 0) {
    formattedNotes = formatNotes(titleNotes);
  }

  // setTimeout needed for Firefox
  setTimeout(function() {
    slides.focus();
  }, 100);

  var notes = w.document.createElement('div');
  notes.id = 'presenter-notes';
  notes.innerHTML = formattedNotes;
  w.document.body.appendChild(notes);

  w.document.close();

  function addPresenterNotesStyle() {
    var el = w.document.createElement('link');
    el.rel = 'stylesheet';
    el.type = 'text/css';
    el.href = PERMANENT_URL_PREFIX + 'notes.css';
    w.document.body.appendChild(el);
    w.document.querySelector('head').appendChild(el);
  }

  addPresenterNotesStyle();

  // Add listener on notesWindow to update notes when triggered from
  // parent window
  w.addEventListener('storage', updateNotes, false);
}

function formatNotes(notes) {
  var formattedNotes = '';
  if (notes) {
    for (var i = 0; i < notes.length; i++) {
      formattedNotes = formattedNotes + '<p>' + notes[i] + '</p>';
    }
  }
  return formattedNotes;
}

function updateNotes() {
  // When triggered from parent window, notesWindow is null
  // The storage event listener on notesWindow will update notes
  if (!notesWindow) return;
  var destSlide = parseInt(localStorage.getItem(destSlideKey()), 10);
  var section = sections[destSlide - 1];
  var el = notesWindow.document.getElementById('presenter-notes');

  if (!el) return;

  if (section && section.Notes) {
    el.innerHTML = formatNotes(section.Notes);
  } else if (destSlide == 0) {
    el.innerHTML = formatNotes(titleNotes);
  } else {
    el.innerHTML = '';
  }
}

/* Playground syncing */

// When presenter notes are enabled, playground click handlers are
// stored here to sync click events on the correct playground
var playgroundHandlers = { onRun: [], onKill: [], onClose: [] };

function updatePlay(e) {
  var i = localStorage.getItem('play-index');

  switch (e.key) {
    case 'play-index':
      return;
    case 'play-action':
      // Sync 'run', 'kill', 'close' actions
      var action = localStorage.getItem('play-action');
      playgroundHandlers[action][i](e);
      return;
    case 'play-code':
      // Sync code editing
      var play = document.querySelectorAll('div.playground')[i];
      play.innerHTML = localStorage.getItem('play-code');
      return;
    case 'output-style':
      // Sync resizing of playground output
      var out = document.querySelectorAll('.output')[i];
      out.style = localStorage.getItem('output-style');
      return;
  }
}

// Reset 'run', 'kill', 'close' storage items when synced
// so that successive actions can be synced correctly
function updatePlayStorage(action, index, e) {
  localStorage.setItem('play-index', index);

  if (localStorage.getItem('play-action') === action) {
    // We're the receiving window, and the message has been received
    localStorage.removeItem('play-action');
  } else {
    // We're the triggering window, send the message
    localStorage.setItem('play-action', action);
  }

  if (action === 'onRun') {
    if (localStorage.getItem('play-shiftKey') === 'true') {
      localStorage.removeItem('play-shiftKey');
    } else if (e.shiftKey) {
      localStorage.setItem('play-shiftKey', e.shiftKey);
    }
  }
}