/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import {SVGDocumentWrapper} from "./svg/SVGDocumentWrapper";
import {Presentation} from "./model/Presentation";
import {Viewport} from "./player/Viewport";
import {Player} from "./player/Player";
import * as Media from "./player/Media";
import * as FrameList from "./player/FrameList";
import * as FrameNumber from "./player/FrameNumber";
import * as FrameURL from "./player/FrameURL";
import * as TouchGestures from "./player/TouchGestures";
/** Put the current player in presenter mode.
*
* This function is used by the presenter console that uses several
* players as *previews* of the previous, current and next frames.
*
* In presenter mode, keyboard and mouse navigation are disabled,
* frame numbers are hidden, hyperlink in the previous and next views are
* disabled, and in the current view, hyperlink ckicks are forwarded to the
* main presentation window.
*
* @param {Window} mainWindow - The browser window that plays the main presentation.
* @param {boolean} isCurrent - Is the current player showing the current frame?
*/
function setPresenterMode(mainWindow, isCurrent) {
sozi.player.disableMedia();
sozi.player.pause();
sozi.presentation.enableMouseTranslation =
sozi.presentation.enableMouseNavigation =
sozi.presentation.enableKeyboardZoom =
sozi.presentation.enableKeyboardRotation =
sozi.presentation.enableKeyboardNavigation = false;
for (let frame of sozi.presentation.frames) {
frame.showFrameNumber = false;
}
if (isCurrent) {
// Forward hyperlink clicks to the main presentation window.
for (let link of sozi.presentation.document.root.getElementsByTagName("a")) {
link.addEventListener("click", evt => {
if (link.id) {
mainWindow.postMessage({name: "click", id: link.id}, "*");
}
evt.preventDefault();
}, false);
}
}
else {
sozi.presentation.document.disableHyperlinks(true);
}
}
/** Process a frame change event.
*
* This event handler is setup when the current player is connected to
* a presenter console.
* On frame change, the event is forwarded to the presenter.
*
* @param {Window} presenterWindow - The window that shows the presenter console.
*
* @listens module:player/Player.frameChange
*/
function onFrameChange(presenterWindow) {
presenterWindow.postMessage({
name : "frameChange",
index: sozi.player.currentFrame.index,
title: sozi.player.currentFrame.title,
notes: sozi.player.currentFrame.notes
}, "*");
}
/** Setup an event handler to forward the frame change event to the presenter window.
*
* This function is called when initializing the connexion between the current
* presentation and a presenter console in another window.
*
* @param {Window} presenterWindow - The window that shows the presenter console.
*/
function notifyOnFrameChange(presenterWindow) {
sozi.player.on("frameChange", () => onFrameChange(presenterWindow));
// Send the message to set the initial frame data in the presenter window.
onFrameChange(presenterWindow);
}
// Process messages from a presenter console.
window.addEventListener("message", evt => {
switch (evt.data.name) {
case "notifyOnFrameChange":
// Install an event handler to forward the frame change event
// to the presenter console.
notifyOnFrameChange(evt.source);
break;
case "setPresenterMode":
// Set this presentation into presenter mode.
setPresenterMode(evt.source, evt.data.isCurrent);
break;
case "click":
// Forward the click event on a hyperlink in the presenter console
// to the same hyperlink in the main presentation.
const link = sozi.presentation.document.root.getElementById(evt.data.id);
// We use dispatchEvent here because
// SVG <a> elements do not have a click method.
link.dispatchEvent(new MouseEvent("click"));
break;
default:
// Interpret a message as a method call to the current Sozi player.
// The message must be of the form: {name: string, args: any[]}.
const method = sozi.player[evt.data.name];
const args = evt.data.args || [];
if (typeof method === "function") {
method.apply(sozi.player, args);
}
else {
console.log(`Unsupported message: ${evt.data.name}`);
}
}
}, false);
// Initialize the Sozi player when the document is loaded.
window.addEventListener("load", () => {
const svgRoot = document.querySelector("svg");
svgRoot.style.display = "initial";
const presentation = new Presentation();
presentation.setSVGDocument(new SVGDocumentWrapper(svgRoot));
const viewport = new Viewport(presentation, false);
viewport.onLoad();
presentation.fromStorable(window.soziPresentationData);
const player = new Player(viewport, presentation);
Media.init(player);
FrameList.init(player);
FrameNumber.init(player);
FrameURL.init(player);
TouchGestures.init(player, presentation);
window.sozi = {
presentation,
viewport,
player
};
player.on("stateChange", () => {
if (player.playing) {
document.title = presentation.title;
}
else {
document.title = presentation.title + " (Paused)";
}
});
window.addEventListener("resize", () => viewport.repaint());
if (presentation.frames.length) {
player.playFromFrame(FrameURL.getFrame());
}
viewport.repaint();
player.disableBlankScreen();
document.querySelector(".sozi-blank-screen .spinner").style.display = "none";
});
/** Identifies the window that opened this presentation.
*
* This constant can typically have three possible values:
* - a presenter console window that opened this window to display the main presentation,
* - a parent window if this presentation is opened in a frame,
* - the current window.
*
* @readonly
* @type {Window}
*/
const opener = window.opener || window.parent;
/** Check that Sozi is loaded and notify a presenter console.
*
* This function will repeatedly check whether the `window.sozi` variable is populated.
* On success, it will send the `loaded` message to the presenter console.
*/
function checkSozi() {
if (window.sozi) {
opener.postMessage({
name: "loaded",
length: sozi.presentation.frames.length,
}, "*");
}
else {
setTimeout(checkSozi, 1);
}
}
if (opener) {
checkSozi();
}