From 3fa27c054a2e66b038557801debfb39700194fd6 Mon Sep 17 00:00:00 2001 From: pacien Date: Wed, 24 Oct 2018 05:03:45 +0200 Subject: Rename project --- beamer/viewer/init.js | 67 +++++++++++++++++++++++ beamer/viewer/presentation.js | 57 +++++++++++++++++++ beamer/viewer/screen/screen.js | 110 +++++++++++++++++++++++++++++++++++++ beamer/viewer/screen/timer.js | 48 ++++++++++++++++ beamer/viewer/stage/actions.js | 121 +++++++++++++++++++++++++++++++++++++++++ beamer/viewer/stage/stage.js | 76 ++++++++++++++++++++++++++ beamer/viewer/viewer.css | 116 +++++++++++++++++++++++++++++++++++++++ beamer/viewer/viewer.js | 65 ++++++++++++++++++++++ 8 files changed, 660 insertions(+) create mode 100644 beamer/viewer/init.js create mode 100644 beamer/viewer/presentation.js create mode 100644 beamer/viewer/screen/screen.js create mode 100644 beamer/viewer/screen/timer.js create mode 100644 beamer/viewer/stage/actions.js create mode 100644 beamer/viewer/stage/stage.js create mode 100644 beamer/viewer/viewer.css create mode 100644 beamer/viewer/viewer.js (limited to 'beamer/viewer') diff --git a/beamer/viewer/init.js b/beamer/viewer/init.js new file mode 100644 index 0000000..9192834 --- /dev/null +++ b/beamer/viewer/init.js @@ -0,0 +1,67 @@ +/* + * Beamer Viewer, a web-based PDF presentation viewer + * Copyright (C) 2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +"use strict"; + +const params = function() { + const queryDict = {}; + location.hash.substr(1).split("&").forEach(item => { + const pair = item.split("="); + queryDict[pair[0]] = pair[1]; + }); + return queryDict; +}(); + +function isController() { + return window.opener == null || window.opener.location.href != window.location.href; +} + +function initCache() { + if (!navigator.serviceWorker) return; + navigator.serviceWorker.register("appcache.js"); + + const offlineCapableIndicator = document.getElementById("offlineCapable"); + offlineCapableIndicator.style.visibility = "visible"; +} + +function checkPopupPermission() { + const popup = window.open("popup.html"); + + if (popup == null) { + const warningMessage = document.getElementById("warning"); + warningMessage.textContent = "A pop-up blocker is active. Make sure to allow pop-ups on this website."; + } +} + +function init() { + initCache(); + checkPopupPermission(); + + const viewer = new Viewer(); + + if ("file" in params) + viewer.load(params["file"]); +} + +function load(file) { + location.hash = "file=" + file; + location.reload(); +} + +if (isController()) + init(); diff --git a/beamer/viewer/presentation.js b/beamer/viewer/presentation.js new file mode 100644 index 0000000..222a6d4 --- /dev/null +++ b/beamer/viewer/presentation.js @@ -0,0 +1,57 @@ +/* + * Beamer Viewer, a web-based PDF presentation viewer + * Copyright (C) 2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +"use strict"; + +class Presentation { + constructor(pdf) { + this.pdf = pdf; + this.currentPageIndex = 1; + this.stage = this._setupStage(); + } + + _setupStage() { + const self = this; + const onStageReadyCallback = function() { self._onStageReady(); }; + const onNextCallback = function() { self._onNext(); }; + const onPreviousCallback = function() { self._onPrevious(); }; + return new Stage(onStageReadyCallback, onNextCallback, onPreviousCallback); + } + + _onStageReady() { + this._setPage(this.currentPageIndex); + } + + _onNext() { + if (this.currentPageIndex === this.pdf.numPages) return; + this._setPage(this.currentPageIndex + 1); + } + + _onPrevious() { + if (this.currentPageIndex === 1) return; + this._setPage(this.currentPageIndex - 1); + } + + _setPage(pageIndex) { + const self = this; + this.currentPageIndex = pageIndex; + this.pdf.getPage(this.currentPageIndex).then(function(page) { + self.stage.setPage(page); + }) + } +} diff --git a/beamer/viewer/screen/screen.js b/beamer/viewer/screen/screen.js new file mode 100644 index 0000000..c65ebc3 --- /dev/null +++ b/beamer/viewer/screen/screen.js @@ -0,0 +1,110 @@ +/* + * Beamer Viewer, a web-based PDF presentation viewer + * Copyright (C) 2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +"use strict"; + +class Screen { + constructor(window, secondary=false, withTimer=false) { + this.window = window; + this.secondary = secondary; + + this.canvasId = "screen"; + this.page = null; + + this.timer = withTimer ? new Timer(window) : null; + this.pageTurnCount = 0; + + this._registerListeners(); + this._hideWelcomeScreen(); + } + + setPage(page) { + if (this.pageTurnCount++ === 1 && this.timer != null) + this.timer.start(); + + this.page = page; + this._repaint(); + } + + _registerListeners() { + const self = this; + this.window.addEventListener("resize", function() { + self._repaint(); + }); + } + + _hideWelcomeScreen() { + const welcomeScreen = this.window.document.getElementById("welcomeScreen"); + welcomeScreen.style.display = "none"; + } + + _getScreenSize(ratio) { + const windowRatio = this.window.innerWidth / this.window.innerHeight; + const horizontalScaleFactor = ratio / windowRatio; + return { + width: this.window.innerWidth * Math.min(horizontalScaleFactor, 1), + height: this.window.innerHeight / Math.max(horizontalScaleFactor, 1) + }; + } + + _getSlideSizeRatio() { + const viewport = this.page.getViewport(1); + return (viewport.width / 2) / viewport.height; + } + + _newCanvas(width, height, xOffset, yOffset) { + const canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + + const context = canvas.getContext("2d"); + context.transform(1, 0, 0, 1, xOffset, yOffset); + + return { canvas: canvas, context: context }; + } + + _showCanvas(canvas) { + const oldCanvas = this.window.document.getElementById(this.canvasId); + canvas.id = oldCanvas.id; + canvas.classList = oldCanvas.classList; + oldCanvas.replaceWith(canvas); + } + + _render(canvas, context, scaleFactor) { + const renderContext = { + canvasContext: context, + viewport: this.page.getViewport(scaleFactor) + }; + + const self = this; + this.page.render(renderContext).then(function() { + self._showCanvas(canvas); + }); + } + + _repaint() { + if (this.page == null) return; + + const screenRatio = this._getSlideSizeRatio(); + const { width, height } = this._getScreenSize(screenRatio); + const scaleFactor = height / this.page.getViewport(1).height; + const xOffset = this.secondary ? -width : 0; + const { canvas, context } = this._newCanvas(width, height, xOffset, 0); + this._render(canvas, context, scaleFactor); + } +} diff --git a/beamer/viewer/screen/timer.js b/beamer/viewer/screen/timer.js new file mode 100644 index 0000000..a04ea63 --- /dev/null +++ b/beamer/viewer/screen/timer.js @@ -0,0 +1,48 @@ +/* + * Beamer Viewer, a web-based PDF presentation viewer + * Copyright (C) 2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +"use strict"; + +class Timer { + constructor(window) { + this.display = window.document.getElementById("timer"); + this.startTime = null; + this._setDisplay(0); + } + + start() { + if (this.startTime != null) return; + this.startTime = Date.now(); + + const self = this; + setInterval(function() { + self._runTimer(); + }, 1000); + } + + _runTimer() { + const timeDelta = Math.floor((Date.now() - this.startTime) / 1000); + this._setDisplay(timeDelta); + } + + _setDisplay(seconds) { + const dateObj = new Date(null); + dateObj.setSeconds(seconds); + this.display.textContent = dateObj.toISOString().substr(11, 8); + } +} diff --git a/beamer/viewer/stage/actions.js b/beamer/viewer/stage/actions.js new file mode 100644 index 0000000..601a441 --- /dev/null +++ b/beamer/viewer/stage/actions.js @@ -0,0 +1,121 @@ +/* + * Beamer Viewer, a web-based PDF presentation viewer + * Copyright (C) 2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +"use strict"; + +class ActionEventHandler { + constructor(onNext, onPrevious) { + this.onNext = onNext; + this.onPrevious = onPrevious; + } +} + +class KeyboardEventHandler extends ActionEventHandler { + register(window) { + const self = this; + window.addEventListener("keydown", function(event) { + self._onCommand(event); + }) + } + + _onCommand(keyboardEvent) { + switch (keyboardEvent.key) { + case "Enter": + case " ": + case "ArrowRight": + case "n": + return this.onNext(); + + case "ArrowLeft": + case "p": + return this.onPrevious(); + } + } +} + +class MouseClickEventHandler extends ActionEventHandler { + register(window) { + const self = this; + window.addEventListener("click", function(event) { + self._onCommand(event); + }) + } + + _onCommand(mouseEvent) { + this.onNext(); + } +} + +class TouchSwipeEventHandler extends ActionEventHandler { + constructor(onNext, onPrevious) { + super(onNext, onPrevious); + this.touchStartEvent = null; + this.touchMoveEvent = null; + } + + register(window) { + const self = this; + + window.addEventListener("touchstart", function(event) { + event.preventDefault(); + self._onTouchStart(event); + }); + + window.addEventListener("touchmove", function(event) { + event.preventDefault(); + self._onTouchMove(event); + }); + + window.addEventListener("touchend", function(event) { + event.preventDefault(); + self._onTouchEnd(); + }); + + window.addEventListener("touchcancel", function(event) { + event.preventDefault(); + }); + } + + _onTouchStart(touchEvent) { + this.touchStartEvent = touchEvent; + } + + _onTouchMove(touchEvent) { + this.touchMoveEvent = touchEvent; + } + + _onTouchEnd() { + if (this.touchStartEvent == null || this.touchMoveEvent == null) return; + + const touchDown = this._xCoordinate(this.touchStartEvent); + const touchUp = this._xCoordinate(this.touchMoveEvent); + const xDelta = touchDown - touchUp; + + if (xDelta > 0) + this.onNext(); + else if (xDelta < 0) + this.onPrevious(); + + this.touchStartEvent = null; + this.touchMoveEvent = null; + } + + _xCoordinate(touchEvent) { + return touchEvent.touches[0].clientX; // first finger + } +} diff --git a/beamer/viewer/stage/stage.js b/beamer/viewer/stage/stage.js new file mode 100644 index 0000000..201f7b4 --- /dev/null +++ b/beamer/viewer/stage/stage.js @@ -0,0 +1,76 @@ +/* + * Beamer Viewer, a web-based PDF presentation viewer + * Copyright (C) 2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +"use strict"; + +class Stage { + constructor(onReady, onNext, onPrevious) { + this.audienceScreen = null; + this.presenterScreen = null; + + this.projector = window.open(window.location.href, "_blank", "toolbar=0,location=0,menubar=0"); + if (this.projector == null) + alert("Please allow pop-ups, then refresh this page."); + + const self = this; + this.projector.addEventListener("load", function() { + self.audienceScreen = new Screen(self.projector, false, false); + self.presenterScreen = new Screen(window, true, true); + self._watchDetach(); + onReady(); + }); + + this.eventHandlers = [ + new KeyboardEventHandler(onNext, onPrevious), + new MouseClickEventHandler(onNext, onPrevious), + new TouchSwipeEventHandler(onNext, onPrevious) + ]; + + this._registerEventHandler(window); + this._registerEventHandler(this.projector); + } + + setPage(page) { + this.audienceScreen.setPage(page); + this.presenterScreen.setPage(page); + } + + _registerEventHandler(window) { + if (window == null) return; + + this.eventHandlers.forEach(function(eventHandler) { + eventHandler.register(window); + }); + } + + _watchDetach() { + const self = this; + window.addEventListener("beforeunload", function() { + self._setMessage(self.projector, "Controller detached"); + }); + + this.projector.addEventListener("beforeunload", function() { + self._setMessage(window, "Viewer detached"); + }); + } + + _setMessage(window, message) { + const messageBar = window.document.getElementById("message"); + messageBar.textContent = message; + } +} diff --git a/beamer/viewer/viewer.css b/beamer/viewer/viewer.css new file mode 100644 index 0000000..a69ddb2 --- /dev/null +++ b/beamer/viewer/viewer.css @@ -0,0 +1,116 @@ +/* + * Beamer Viewer, a web-based PDF presentation viewer + * Copyright (C) 2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +html, body { + background-color: black; + color: white; + font-family: sans-serif; + overflow: hidden; + touch-action: none; + height: 100%; +} + +a { + color: white; +} + +header > h1 { + display: inline-block; + margin: 0 0.5rem 0 0; +} + +header span { + font-size: 1.25rem; +} + +#offlineCapable { + visibility: hidden; +} + +#welcomeScreen { + max-height: 100%; + min-width: 20rem; + overflow: auto; + padding: 1rem; + box-sizing: border-box; +} + +#welcomeScreen > div { + margin-bottom: 4rem; +} + +#welcomeScreen > div:last-child { + margin-bottom: 1rem; +} + +#welcomeScreen table { + width: 100%; + border-collapse: collapse; +} + +#welcomeScreen table td { + text-align: center; + border: 1px solid white; + width: 50%; + height: 8rem; +} + +#welcomeScreen ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +#welcomeScreen li { + display: inline; +} + +#welcomeScreen li + li::before { + content: " • "; +} + +#fileInput { + width: 100%; +} + +.centered { + position: absolute; + top: 50%; + left: 50%; + transform: translateX(-50%) translateY(-50%); +} + +.notification { + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + padding: 0.5rem 1rem; + border-radius: 1rem 1rem 0 0; + background-color: black; + font-size: 1.6rem; +} + +.notification:empty { + display: none; +} + +.warning { + font-weight: bold; + color: orangered; +} diff --git a/beamer/viewer/viewer.js b/beamer/viewer/viewer.js new file mode 100644 index 0000000..414ff1c --- /dev/null +++ b/beamer/viewer/viewer.js @@ -0,0 +1,65 @@ +/* + * Beamer Viewer, a web-based PDF presentation viewer + * Copyright (C) 2018 Pacien TRAN-GIRARD + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +"use strict"; + +class Viewer { + constructor() { + this.fileInput = document.getElementById("fileInput"); + this._listenForInput(); + } + + load(source) { + pdfjsLib.getDocument(source).then(function(pdf) { + const presentation = new Presentation(pdf); + }).catch(function(error) { + console.error(error); + window.alert("Error while loading presentation:\n\n" + error.message); + window.location.href = window.location.pathname; // reload without "?file=..." + }); + } + + _readFile(file) { + const fileReader = new FileReader(); + const self = this; + fileReader.onload = function() { + const byteArray = new Uint8Array(this.result); + self.load(byteArray); + } + + fileReader.readAsArrayBuffer(file); + } + + _listenForInput() { + const self = this; + fileInput.addEventListener("change", function(event) { + self._readFile(event.target.files[0]); + }); + + document.body.addEventListener("drop", function(event) { + event.preventDefault(); + event.stopPropagation(); + self._readFile(event.dataTransfer.files[0]); + }); + + document.body.addEventListener("dragover", function(event) { + event.preventDefault(); + event.stopPropagation(); + }); + } +} -- cgit v1.2.3