UNPKG

@uriopass/nosleep.js

Version:

Prevent display sleep and enable wake lock in any Android or iOS web browser

154 lines (141 loc) 4.57 kB
const { webm, mp4 } = require("./media.js"); // Detect iOS browsers < version 10 const oldIOS = () => typeof navigator !== "undefined" && parseFloat( ( "" + (/CPU.*OS ([0-9_]{3,4})[0-9_]{0,1}|(CPU like).*AppleWebKit.*Mobile/i.exec( navigator.userAgent ) || [0, ""])[1] ) .replace("undefined", "3_2") .replace("_", ".") .replace("_", "") ) < 10 && !window.MSStream; // Detect native Wake Lock API support (Samsung Browser supports it but cannot use it) const nativeWakeLock = () => "wakeLock" in navigator && window.navigator.userAgent.indexOf("Samsung") === -1; class NoSleep { constructor() { this.enabled = false; if (nativeWakeLock()) { this._wakeLock = null; const handleVisibilityChange = () => { if (this._wakeLock !== null && document.visibilityState === "visible") { this.enable(); } }; document.addEventListener("visibilitychange", handleVisibilityChange); document.addEventListener("fullscreenchange", handleVisibilityChange); } else if (oldIOS()) { this.noSleepTimer = null; } else { // Set up no sleep video element this.noSleepVideo = document.createElement("video"); this.noSleepVideo.setAttribute("title", "No Sleep"); this.noSleepVideo.setAttribute("playsinline", ""); this._addSourceToVideo(this.noSleepVideo, "webm", webm); this._addSourceToVideo(this.noSleepVideo, "mp4", mp4); // For iOS >15 video needs to be on the document to work as a wake lock Object.assign(this.noSleepVideo.style, { position: "absolute", left: "-100%", top: "-100%", }); document.querySelector("body").append(this.noSleepVideo); this.noSleepVideo.addEventListener("loadedmetadata", () => { if (this.noSleepVideo.duration <= 1) { // webm source this.noSleepVideo.setAttribute("loop", ""); } else { // mp4 source this.noSleepVideo.addEventListener("timeupdate", () => { if (this.noSleepVideo.currentTime > 0.5) { this.noSleepVideo.currentTime = Math.random(); } }); } }); } } _addSourceToVideo(element, type, dataURI) { var source = document.createElement("source"); source.src = dataURI; source.type = `video/${type}`; element.appendChild(source); } get isEnabled() { return this.enabled; } enable() { if (nativeWakeLock()) { return navigator.wakeLock .request("screen") .then((wakeLock) => { this._wakeLock = wakeLock; this.enabled = true; console.log("Wake Lock active."); this._wakeLock.addEventListener("release", () => { // ToDo: Potentially emit an event for the page to observe since // Wake Lock releases happen when page visibility changes. // (https://web.dev/wakelock/#wake-lock-lifecycle) console.log("Wake Lock released."); }); }) .catch((err) => { this.enabled = false; console.error(`${err.name}, ${err.message}`); throw err; }); } else if (oldIOS()) { this.disable(); console.warn(` NoSleep enabled for older iOS devices. This can interrupt active or long-running network requests from completing successfully. See https://github.com/richtr/NoSleep.js/issues/15 for more details. `); this.noSleepTimer = window.setInterval(() => { if (!document.hidden) { window.location.href = window.location.href.split("#")[0]; window.setTimeout(window.stop, 0); } }, 15000); this.enabled = true; return Promise.resolve(); } else { let playPromise = this.noSleepVideo.play(); return playPromise .then((res) => { this.enabled = true; return res; }) .catch((err) => { this.enabled = false; throw err; }); } } disable() { if (nativeWakeLock()) { if (this._wakeLock) { this._wakeLock.release(); } this._wakeLock = null; } else if (oldIOS()) { if (this.noSleepTimer) { console.warn(` NoSleep now disabled for older iOS devices. `); window.clearInterval(this.noSleepTimer); this.noSleepTimer = null; } } else { this.noSleepVideo.pause(); } this.enabled = false; } } module.exports = NoSleep;