Add picture-in-picture mode

This commit is contained in:
Reimar 2025-08-06 19:21:14 +02:00
parent c65310e890
commit ff5f60f765
Signed by: Reimar
GPG Key ID: 93549FA07F0AE268
4 changed files with 98 additions and 9 deletions

View File

@ -11,6 +11,7 @@
<link rel="canonical" href="https://popup-timer.reim.ar/" />
<link rel="stylesheet" href="/style.css">
<link rel="shortcut icon" href="/favicon.ico" />
<script src="/pip.js" defer></script>
<script src="/script.js" defer></script>
</head>
<body>
@ -70,8 +71,28 @@
</div>
</main>
<br><br>
<span id="link" title="Click to copy"></span>
<details id="advanced">
<summary>Advanced</summary>
<div style="margin-top: 20px">
<span id="link" title="Click to copy"></span>
<p class="desc">
Pasting this link in your browser will make the timer/stopwatch immediately pop up.
</p>
</div>
<div id="pip" style="margin-top: 40px">
<label>
<input id="pip-enabled" type="checkbox"> Enable Picture-in-picture mode (experimental)
</label>
<i class="fas fa-flask"></i>
<br>
<p class="desc">
If enabled, the window will always stay on top, but requires that this tab stay open.
</p>
</div>
</details>
</body>
</html>

35
pip.js Normal file
View File

@ -0,0 +1,35 @@
function pipSupported() {
return window.documentPictureInPicture && document.pictureInPictureEnabled;
}
function pipEnabled() {
return pipSupported() && localStorage.getItem("pip-enabled") != 0;
}
function setPipEnabled(enabled) {
localStorage.setItem("pip-enabled", +enabled);
}
function openPipWindow(link, w, h) {
return new Promise(function(resolve) {
window.documentPictureInPicture.requestWindow({ width: w, height: h }).then(function(win) {
var iframe = document.createElement("iframe");
iframe.src = link;
iframe.style.width = w + "px";
iframe.style.height = h + "px";
iframe.style.border = "none";
win.document.body.style.margin = "0";
win.document.body.style.overflow = "hidden";
win.document.body.appendChild(iframe);
win.onpagehide = resolve;
win.onresize = function() {
iframe.style.width = win.innerWidth + "px";
iframe.style.height = win.innerHeight + "px";
}
});
});
}

View File

@ -1,12 +1,20 @@
// Make checkboxes green on Chrome
/*if (navigator.vendor === "Google Inc.") {
[].slice.call(document.querySelectorAll("input[type='checkbox']")).forEach(function(checkbox) {
checkbox.style.filter = "invert(100%) hue-rotate(70deg) brightness(0.8)";
});
}*/
// Picture-in-picture
document.getElementById("pip-enabled").checked = pipEnabled();
document.getElementById("pip-enabled").oninput = function(event) {
setPipEnabled(event.target.checked);
}
// Open new window when clicking create
document.getElementById("create").onclick = function(event) {
if (pipEnabled()) {
event.target.disabled = true;
openPipWindow(createLink("popup"), 250, 100).then(function() {
event.target.disabled = false;
});
return;
}
var popup = window.open(
createLink("popup"),
@ -98,7 +106,9 @@ function showBookmarkAdded() {
}
document.getElementById("input-common").style.display = "block";
document.getElementById("advanced").style.display = "block";
document.getElementById("welcome").style.display = "none";
document.getElementById("pip").style.display = pipSupported() ? "block" : "none";
var linkElem = document.getElementById("link");
linkElem.innerText = createLink("link");
@ -110,7 +120,7 @@ function showBookmarkAdded() {
});
// Disable buttons on invalid input
[].slice.call(document.getElementsByTagName("input")).forEach(function(input) {
[].slice.call(document.getElementsByTagName("#input-timer input, #input-common input")).forEach(function(input) {
input.oninput = function(event) {
var createBtn = document.getElementById("create");

View File

@ -139,4 +139,27 @@ input[type=checkbox] {
margin-bottom: 14px;
margin-top: 0;
}
#pip {
display: none;
font-size: 14px;
margin-top: 60px;
}
#advanced {
font-size: 14px;
display: none;
margin-top: 40px;
}
#advanced summary {
cursor: pointer;
color: #757575;
transition: color 200ms;
}
#advanced summary:hover {
color: black;
}
.desc {
margin: 8px 0;
color: #757575;
font-size: 12px;
}