diff --git a/webapp/public/js/domjudge.js b/webapp/public/js/domjudge.js index e3b9250c3b..527433fcb3 100644 --- a/webapp/public/js/domjudge.js +++ b/webapp/public/js/domjudge.js @@ -1292,3 +1292,97 @@ function initScoreboardSubmissions() { }); }); } + +$(function () { + window.checkExperimentalFeature = function () { + if ("documentPictureInPicture" in window) { + // Only the team interface has this button. + const togglePipButton = document.querySelector("#pop-out-button"); + if (togglePipButton) { + togglePipButton.style.display = 'inline'; + togglePipButton.addEventListener("click", togglePictureInPicture, false); + } + } else { + const pipMessage = document.querySelector("#no-picture-in-picture"); + if (pipMessage) { + pipMessage.style.display = 'inline'; + } + } + }; + checkExperimentalFeature(); + + // Heavily based on: https://github.com/mdn/dom-examples/tree/main/document-picture-in-picture + const popOutFrame = document.querySelector("#pop-out-frame"); + const popOutContainer = document.querySelector("#pop-out-container"); + async function togglePictureInPicture() { + if (!(popOutFrame && popOutContainer)) { + console.log("Unexpected page, we only run if both are available."); + return; + } + + popOutContainer.style.display = 'block'; + // Early return if there's already a Picture-in-Picture window open + if (window.documentPictureInPicture.window) { + popOutContainer.append(popOutFrame); + window.documentPictureInPicture.window.close(); + return; + } + + // Open a Picture-in-Picture window. + const pipWindow = await window.documentPictureInPicture.requestWindow({ + width: popOutFrame.clientWidth, + height: popOutFrame.clientHeight + }); + + pipWindow.document.body.style.padding = 0; + pipWindow.document.body.style.margin = 0; + const scoreBoards = document.querySelectorAll("#pop-out-frame .scoreboard"); + scoreBoards.forEach((scoreBoard) => { + scoreBoard.style.margin = 0; + }); + console.log("sb", scoreBoards); + + // Add pagehide listener to handle the case of the pip window being closed using the browser X button + pipWindow.addEventListener("pagehide", (event) => { + popOutContainer.style.display = 'none'; + popOutContainer.append(popOutFrame); + }); + + + // Copy style sheets over from the initial document + // so that the player looks the same. + [...document.styleSheets].forEach((styleSheet) => { + try { + if (styleSheet.href) { + const link = document.createElement("link"); + + link.rel = "stylesheet"; + link.type = styleSheet.type; + link.media = styleSheet.media; + link.href = styleSheet.href; + + pipWindow.document.head.appendChild(link); + } else { + const cssRules = [...styleSheet.cssRules] + .map((rule) => rule.cssText) + .join(""); + const style = document.createElement("style"); + + style.textContent = cssRules; + pipWindow.document.head.appendChild(style); + } + } catch (e) { + const link = document.createElement("link"); + + link.rel = "stylesheet"; + link.type = styleSheet.type; + link.media = styleSheet.media; + link.href = styleSheet.href; + + pipWindow.document.head.appendChild(link); + } + }); + + pipWindow.document.body.append(popOutFrame); + } +}); diff --git a/webapp/public/style_domjudge.css b/webapp/public/style_domjudge.css index 18eb31ef24..f81335392b 100644 --- a/webapp/public/style_domjudge.css +++ b/webapp/public/style_domjudge.css @@ -757,3 +757,15 @@ blockquote { height: 80vh; border: 1px solid grey; } + +#pop-out-button { + display: none; +} + +#no-picture-in-picture { + display: none; +} + +#pop-out-container { + display: none; +} \ No newline at end of file diff --git a/webapp/templates/team/index.html.twig b/webapp/templates/team/index.html.twig index ae6244810d..b735aa6f64 100644 --- a/webapp/templates/team/index.html.twig +++ b/webapp/templates/team/index.html.twig @@ -24,7 +24,7 @@ {% block messages %}{% endblock %} {% block content %} -
+
{% include 'team/partials/index_content.html.twig' %}
{% endblock %} @@ -37,7 +37,7 @@ $flash = $('[data-flash-messages]').children(); } - function setFlashAndProgress() { + function setFlashAndProgressAndExperimentalFeatures() { var $newProgress = $('[data-ajax-refresh-target] > [data-progress-bar]'); if ($newProgress.length) { var $oldProgress = $('body > [data-progress-bar]'); @@ -46,6 +46,8 @@ } $('[data-flash-messages]').html($flash); + + checkExperimentalFeature(); } window.initModalClarificationPreviewAdd = function() { diff --git a/webapp/templates/team/partials/index_content.html.twig b/webapp/templates/team/partials/index_content.html.twig index 34c7ca453c..f2fd898d3b 100644 --- a/webapp/templates/team/partials/index_content.html.twig +++ b/webapp/templates/team/partials/index_content.html.twig @@ -46,6 +46,23 @@ Request clarification
+

Experimental feature

+
+

+ Ask your Tech team for another browser (Document Picture-in-Picture API not available +

+ + PR Pop-out + +

+
+ {% set displayRank = not contest.freezeData.showFrozen %} + {% include 'partials/scoreboard_table.html.twig' with {displayRank: displayRank, jury: false, public: false} %} + 3 + C++/warning +
+
+
{% endif %}