diff options
Diffstat (limited to 'app/index.php')
-rw-r--r-- | app/index.php | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/app/index.php b/app/index.php new file mode 100644 index 0000000..e6d93ff --- /dev/null +++ b/app/index.php @@ -0,0 +1,551 @@ +<?php require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $_PROFILE; ?> +<!doctype html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" + content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> + <meta http-equiv="X-UA-Compatible" content="ie=edge"> + <title>Mist</title> + <!--<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">--> + <link href="/assets/dark.css" rel="stylesheet"> + <link href="/assets/styles.css" rel="stylesheet"> + <!--<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>--> + <script src="/assets/localforage.min.js"></script> + <script src="/assets/shortcuts.js"></script> + <link rel="shortcut icon" href="/assets/logo-display.svg" type="image/svg+xml"> + <link rel="manifest" href="/manifest.json" /> + <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)"> + <meta name="theme-color" content="#000000" media="(prefers-color-scheme: dark)"> + <meta name="apple-mobile-web-app-status-bar" content="#ffffff" media="(prefers-color-scheme: light)"> + <meta name="apple-mobile-web-app-status-bar" content="#000000" media="(prefers-color-scheme: dark)"> + <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" media="(prefers-color-scheme: dark)"> + <meta name="apple-mobile-web-app-status-bar-style" content="white-translucent" media="(prefers-color-scheme: light)"> + <meta name="description" content="Mist Audio Player"> +</head> +<body> + <script> + if (location.hash.trim() === "") location.hash = "#/albums"; + + if (window.MistNative) { + MistNative.version("<?= explode("|", trim(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/version")))[0] ?>", "<?= trim(file_get_contents("/opt/spotify/build.txt")) ?>"); + MistNative.userInfo(`<?= str_replace("`", "\\`", json_encode($_PROFILE)) ?>`); + } + </script> + <div id="loading" style="z-index: 999999; position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; background-color: white;"> + <span id="loading-text">Initializing...</span> + </div> + + <iframe title="Player" id="player" src="player.php" style="position: fixed; top: 0; left: 320px; right: 0; width: calc(100vw - 320px); height: 64px; border-bottom: 1px solid rgba(0, 0, 0, .25); z-index: 9999;"></iframe> + <iframe title="Navigation" id="navigation" src="navigation.php" style="position: fixed; top: 0; bottom: 0; left: 0; height: 100vh; width: 320px; z-index: 9999;"></iframe> + <iframe title="UI" id="ui" style="position: fixed; top: 65px; bottom: 0; left: 320px; height: calc(100vh - 64px); width: calc(100vw - 320px); z-index: 9999;"></iframe> + <iframe title="Lyrics" id="lyrics-page" src="lyrics.php" style="display: none; position: fixed; top: 64px; bottom: 0; left: 320px; height: calc(100vh - 64px); width: calc(100vw - 320px); z-index: 9999;"></iframe> + <div id="player-mobile-container" style="background-color: white; position: fixed; left: 0; right: 0; height: 100vh; bottom: -100vh; transition: bottom 200ms; z-index: 99999;"> + <iframe title="Mobile player" style="background: #ddd; width: 100%; height: 100%;" src="player-mobile.php" id="player-mobile"></iframe> + </div> + + <script> + window.playlist = []; + + window.showLyrics = () => { + location.hash = "#/lyrics"; + document.getElementById("lyrics-page").style.display = ""; + document.getElementById("ui").style.display = "none"; + Array.from(document.getElementById("navigation").contentDocument.getElementsByClassName("navigation-item")).map(i => i.classList.remove("active")); + document.getElementById("navigation").contentDocument.getElementById("lyrics").classList.add("active"); + } + + function openUI(name) { + hideMobilePlayer(); + location.hash = "#/" + name; + document.getElementById("lyrics-page").style.display = "none"; + document.getElementById("ui").style.display = ""; + document.getElementById("ui").src = name + ".php"; + Array.from(document.getElementById("navigation").contentDocument.getElementsByClassName("navigation-item")).map(i => i.classList.remove("active")); + document.getElementById("navigation").contentDocument.getElementById(name).classList.add("active"); + } + + let name = location.hash.split("/")[1]; + + if (name === "lyrics") { + document.getElementById("ui").src = "albums.php"; + } else if (name === "albums" && location.hash.split("/")[2]) { + document.getElementById("ui").src = "listing.php?a=" + location.hash.split("/")[2]; + } else { + document.getElementById("ui").src = name + ".php"; + } + + document.getElementById("navigation").onload = () => { + if (name === "lyrics") showLyrics(); + Array.from(document.getElementById("navigation").contentDocument.getElementsByClassName("navigation-item")).map(i => i.classList.remove("active")); + document.getElementById("navigation").contentDocument.getElementById(name).classList.add("active"); + } + + let loadedPlayers = 0; + + document.getElementById("player").onload = document.getElementById("player-mobile").onload = () => { + loadedPlayers++; + if (loadedPlayers === 2) continueLoading(); + } + + window.onresize = window.onload = () => { + if (window.innerWidth <= 863) { + document.getElementById("player").contentDocument.getElementById("player").classList.add("mobilified"); + } else { + document.getElementById("player").contentDocument.getElementById("player").classList.remove("mobilified"); + } + } + + function continueLoading() { + window.playerDocument = document.getElementById("player").contentDocument; + window.playerDocumentMobile = document.getElementById("player-mobile").contentDocument; + + if (!localStorage.getItem("data-saving")) { + localStorage.setItem("data-saving", "false"); + } + + if (!localStorage.getItem("desktop-notification")) { + localStorage.setItem("desktop-notification", "true"); + } + + playerDocument.getElementById("seekbar-container").onclick = (e) => { + playerDocument.getElementById("player-audio").currentTime = (e.offsetX / playerDocument.getElementById("seekbar-container").clientWidth) * playerDocument.getElementById("player-audio").duration; + } + + playerDocumentMobile.getElementById("seekbar-container").onclick = (e) => { + playerDocument.getElementById("player-audio").currentTime = (e.offsetX / playerDocumentMobile.getElementById("seekbar-container").clientWidth) * playerDocument.getElementById("player-audio").duration; + } + + function parseTime(subject, max) { + let minutesLength = Math.ceil(max / 60).toString().length; + + let minutes = Math.floor(subject / 60); + let seconds = Math.floor(subject - (minutes * 60)); + + let minutesStr = "0".repeat(minutesLength).substring(0, minutesLength - minutes.toString().length) + minutes.toString(); + let secondsStr = "00".substring(0, 2 - seconds.toString().length) + seconds.toString(); + + return minutesStr + ":" + secondsStr; + } + + playerDocument.getElementById("player-audio").onended = () => { + next(); + } + + window.stop = () => { + document.title = "Mist"; + window.currentSong = null; + window.currentSongID = null; + window.currentPlaylistID = null; + window.playlist = []; + + document.getElementById("player").contentWindow.location.reload(); + document.getElementById("player").onload = () => { + window.playerDocument = document.getElementById("player").contentDocument; + + playerDocument.getElementById("player-audio").ontimeupdate = playerDocument.getElementById("player-audio").onchange = playerDocument.getElementById("player-audio").onunload = playerDocument.getElementById("player-audio").onstop = playerDocument.getElementById("player-audio").onplay = playerDocument.getElementById("player-audio").onpause = () => { + updateDisplay(); + } + } + + document.getElementById("player-mobile").contentWindow.location.reload(); + document.getElementById("player-mobile").onload = () => { + window.playerDocumentMobile = document.getElementById("player-mobile").contentDocument; + } + + hideMobilePlayer(); + } + + window.showMobilePlayer = () => { + if (window.currentSongID !== null) { + document.getElementById("player-mobile-container").style.bottom = "0"; + document.getElementById("lyrics-page").classList.add("mobile-show"); + } + } + + window.hideMobilePlayer = () => { + document.getElementById("player-mobile-container").style.bottom = "-100vh"; + document.getElementById("lyrics-page").classList.remove("mobile-show"); + } + + document.getElementById("player-mobile-container").onclick = (e) => { + if (e.target.id === "player-mobile-container") { + hideMobilePlayer(); + } + } + + window.currentPlaylistPosition = 0; + + window.next = () => { + if (window.repeat) { + playlist.push(playlist[currentPlaylistPosition]); + } + + if (playlist[currentPlaylistPosition + 1]) { + playSong(playlist[currentPlaylistPosition + 1], "keep"); + currentPlaylistPosition++; + } else { + stop(); + } + } + + window.previous = () => { + if (playlist[currentPlaylistPosition - 1]) { + playSong(playlist[currentPlaylistPosition - 1], "keep"); + currentPlaylistPosition--; + } else { + stop(); + } + } + + playerDocument.getElementById("player-audio").ontimeupdate = playerDocument.getElementById("player-audio").onchange = playerDocument.getElementById("player-audio").onunload = playerDocument.getElementById("player-audio").onstop = playerDocument.getElementById("player-audio").onplay = playerDocument.getElementById("player-audio").onpause = () => { + updateDisplay(); + } + + function updateDisplay() { + if (playerDocument.getElementById("player-audio").paused) { + document.title = "Mist"; + } else if (currentSong) { + document.title = currentSong.artist + " — " + currentSong.title; + } else { + document.title = "Mist"; + } + + playerDocument.getElementById("info").style.display = "grid"; + playerDocument.getElementById("cover").style.display = "none"; + + document.getElementById("player").contentWindow.navigator.mediaSession.playbackState = playerDocument.getElementById("player-audio").paused ? "paused" : "playing"; + let state = { + duration: isFinite(playerDocument.getElementById("player-audio").duration) ? playerDocument.getElementById("player-audio").duration : 0, + position: playerDocument.getElementById("player-audio").currentTime, + playbackRate: 1 + } + document.getElementById("player").contentWindow.navigator.mediaSession.setPositionState(state); + + document.getElementById("player").contentWindow.navigator.mediaSession.setActionHandler("play", () => { + playPause(); + }); + document.getElementById("player").contentWindow.navigator.mediaSession.setActionHandler("pause", () => { + playPause(); + }); + document.getElementById("player").contentWindow.navigator.mediaSession.setActionHandler("stop", () => { + stop(); + }); + document.getElementById("player").contentWindow.navigator.mediaSession.setActionHandler("seekbackward", (e) => { + let time = e.seekOffset ?? 10; + + if (playerDocument.getElementById("player-audio").currentTime >= time) { + playerDocument.getElementById("player-audio").currentTime -= time; + } else { + playerDocument.getElementById("player-audio").currentTime = 0; + } + }); + document.getElementById("player").contentWindow.navigator.mediaSession.setActionHandler("seekforward", (e) => { + let time = e.seekOffset ?? 10; + + if (playerDocument.getElementById("player-audio").currentTime + time < playerDocument.getElementById("player-audio").duration) { + playerDocument.getElementById("player-audio").currentTime += time; + } else { + next(); + } + }); + document.getElementById("player").contentWindow.navigator.mediaSession.setActionHandler("seekto", (e) => { + if (e.seekTime) { + playerDocument.getElementById("player-audio").currentTime = e.seekTime; + } + }); + document.getElementById("player").contentWindow.navigator.mediaSession.setActionHandler("previoustrack", () => { + if (playlist[currentPlaylistPosition - 1]) { + previous(); + } else { + playerDocument.getElementById("player-audio").currentTime = 0; + } + }); + document.getElementById("player").contentWindow.navigator.mediaSession.setActionHandler("nexttrack", () => { + next(); + }); + + if (currentPlaylistPosition === 0) { + playerDocument.getElementById("btn-previous").classList.add("disabled"); + playerDocumentMobile.getElementById("btn-previous").classList.add("disabled"); + } else { + playerDocument.getElementById("btn-previous").classList.remove("disabled"); + playerDocumentMobile.getElementById("btn-previous").classList.remove("disabled"); + } + + if (currentPlaylistPosition === playlist.length - 1) { + playerDocument.getElementById("btn-next").classList.add("disabled"); + playerDocumentMobile.getElementById("btn-next").classList.add("disabled"); + } else { + playerDocument.getElementById("btn-next").classList.remove("disabled"); + playerDocumentMobile.getElementById("btn-next").classList.remove("disabled"); + } + + if (isFinite(playerDocument.getElementById("player-audio").duration)) { + playerDocument.getElementById("elapsed-time").innerText = playerDocumentMobile.getElementById("elapsed-time").innerText = parseTime(playerDocument.getElementById("player-audio").currentTime, playerDocument.getElementById("player-audio").duration); + playerDocument.getElementById("remaining-time").innerText = playerDocumentMobile.getElementById("remaining-time").innerText = "-" + parseTime(playerDocument.getElementById("player-audio").duration - playerDocument.getElementById("player-audio").currentTime, playerDocument.getElementById("player-audio").duration); + } else { + playerDocument.getElementById("elapsed-time").innerText = playerDocumentMobile.getElementById("elapsed-time").innerText = parseTime(0, currentSong.length); + playerDocument.getElementById("remaining-time").innerText = playerDocumentMobile.getElementById("remaining-time").innerText = "-" + parseTime(currentSong.length, currentSong.length); + } + + if (playerDocument.getElementById("player-audio").paused) { + playerDocument.getElementById("btn-play-icon").src = playerDocumentMobile.getElementById("btn-play-icon").src = "/assets/icons/play.svg"; + } else { + playerDocument.getElementById("btn-play-icon").src = playerDocumentMobile.getElementById("btn-play-icon").src = "/assets/icons/pause.svg"; + } + + if (localStorage.getItem("data-saving") === "true") { + playerDocument.getElementById("badge-lossy").style.display = "inline"; + playerDocument.getElementById("badge-cd").style.display = "none"; + playerDocument.getElementById("badge-hires").style.display = "none"; + playerDocumentMobile.getElementById("badge-lossy").style.display = "inline"; + playerDocumentMobile.getElementById("badge-cd").style.display = "none"; + playerDocumentMobile.getElementById("badge-hires").style.display = "none"; + } else { + if (window.currentSong && window.currentSong.hiRes) { + playerDocument.getElementById("badge-lossy").style.display = "none"; + playerDocument.getElementById("badge-cd").style.display = "none"; + playerDocument.getElementById("badge-hires").style.display = "inline"; + playerDocument.getElementById("badge-hires").innerHTML = "<span style='display: grid; grid-template-columns: max-content max-content'><span><img src='/assets/icons/lossless.svg' alt='' class='player-badge-icon' style='filter: invert(1);'>Hi-Res Lossless</span><span class='player-badge-desktop'>" + window.currentSong.bitDepth + "-bit " + (window.currentSong.sampleRate / 1000).toFixed(1) + " kHz</span>"; + playerDocumentMobile.getElementById("badge-lossy").style.display = "none"; + playerDocumentMobile.getElementById("badge-cd").style.display = "none"; + playerDocumentMobile.getElementById("badge-hires").style.display = "inline"; + } else if (window.currentSong) { + playerDocument.getElementById("badge-lossy").style.display = "inline"; + playerDocument.getElementById("badge-cd").style.display = "inline"; + playerDocument.getElementById("badge-hires").style.display = "none"; + playerDocumentMobile.getElementById("badge-lossy").style.display = "inline"; + playerDocumentMobile.getElementById("badge-cd").style.display = "inline"; + playerDocumentMobile.getElementById("badge-hires").style.display = "none"; + playerDocument.getElementById("badge-cd").innerHTML = "<span style='display: grid; grid-template-columns: max-content max-content;'>'><span><img src='/assets/icons/lossless.svg' alt='' class='player-badge-icon' style='filter: invert(1);'>Lossless</span><span class='player-badge-desktop'>" + window.currentSong.bitDepth + "-bit " + (window.currentSong.sampleRate / 1000).toFixed(1) + " kHz</span>"; + } + } + + if (window.currentSong) { + playerDocument.getElementById("title").innerText = playerDocumentMobile.getElementById("title").innerText = window.currentSong.title; + playerDocument.getElementById("artist").innerText = playerDocumentMobile.getElementById("artist").innerText = window.currentSong.artist; + playerDocument.getElementById("album").innerText = playerDocumentMobile.getElementById("album").innerText = window.currentSong.album; + playerDocument.getElementById("album-art").src = playerDocumentMobile.getElementById("album-art").src = "/assets/content/" + window.currentSongID + ".jpg"; + playerDocumentMobile.getElementById("album-art-bg").style.backgroundImage = "url('/assets/content/" + window.currentSongID + ".jpg')"; + } + + if (isFinite(playerDocument.getElementById("player-audio").duration)) { + playerDocument.getElementById("seekbar").style.width = playerDocumentMobile.getElementById("seekbar").style.width = ((playerDocument.getElementById("player-audio").currentTime / playerDocument.getElementById("player-audio").duration) * 100) + "%"; + } else { + playerDocument.getElementById("seekbar").style.width = playerDocumentMobile.getElementById("seekbar").style.width = "0"; + } + } + + window.playPause = () => { + if (playlist.length === 0) return; + + if (playerDocument.getElementById("player-audio").paused) { + playerDocument.getElementById("player-audio").play(); + } else { + playerDocument.getElementById("player-audio").pause(); + } + } + + window.redownloadFavorite = async () => { + document.getElementById("loading-text").innerText = "Downloading favorites..."; + window.favorites = await (await fetch("/api/getFavorites.php")).json(); + } + + window.redownloadLibrary = async () => { + document.getElementById("loading-text").innerText = "Downloading library..."; + window.favorites = await (await fetch("/api/getLibrary.php")).json(); + } + + (async () => { + document.getElementById("loading-text").innerText = "Downloading list of songs..."; + window.songs = await (await fetch("/assets/content/songs.json")).json(); + + document.getElementById("loading-text").innerText = "Downloading list of albums..."; + window.albums = await (await fetch("/assets/content/albums.json")).json(); + + document.getElementById("loading-text").innerText = "Downloading favorites..."; + window.favorites = await (await fetch("/api/getFavorites.php")).json(); + + document.getElementById("loading-text").innerText = "Saving database..."; + await localforage.setItem("albums", window.albums); + await localforage.setItem("songs", window.songs); + await localforage.setItem("favorites", window.favorites); + + document.getElementById("loading-text").innerText = "Done loading."; + document.getElementById("loading").style.display = "none"; + })(); + + window.currentSong = null; + window.currentSongID = null; + window.preloaded = {}; + window.shuffle = false; + window.repeat = false; + + window.toggleRepeat = () => { + window.repeat = !window.repeat; + + if (window.repeat) { + playerDocument.getElementById("btn-repeat-icon").src = playerDocumentMobile.getElementById("btn-repeat-icon").src = "/assets/icons/repeat-on.svg"; + } else { + playerDocument.getElementById("btn-repeat-icon").src = playerDocumentMobile.getElementById("btn-repeat-icon").src = "/assets/icons/repeat-off.svg"; + } + } + + function shuffleArray(array) { + let currentIndex = array.length, temporaryValue, randomIndex; + + while (0 !== currentIndex) { + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex -= 1; + + temporaryValue = array[currentIndex]; + array[currentIndex] = array[randomIndex]; + array[randomIndex] = temporaryValue; + } + + return array; + } + + window.toggleShuffle = () => { + window.shuffle = !window.shuffle; + + if (window.shuffle) { + window.playlist = shuffleArray(window.playlist); + playerDocument.getElementById("btn-shuffle-icon").src = playerDocumentMobile.getElementById("btn-shuffle-icon").src = "/assets/icons/shuffle-on.svg"; + } else { + if (currentPlaylistID) { + if (currentPlaylistID === "favorites") { + window.playlist = favorites; + } else if (currentPlaylistID.startsWith("album:")) { + window.playlist = albums[currentPlaylistID.substring(6)].tracks; + } else if (currentPlaylistID !== "keep") { + window.playlist = [currentSongID]; + } + } else { + window.playlist = [currentSongID]; + } + + playerDocument.getElementById("btn-shuffle-icon").src = playerDocumentMobile.getElementById("btn-shuffle-icon").src = "/assets/icons/shuffle-off.svg"; + } + } + + window.shuffleList = (playlistID) => { + window.shuffle = true; + playerDocument.getElementById("btn-shuffle-icon").src = playerDocumentMobile.getElementById("btn-shuffle-icon").src = "/assets/icons/shuffle-on.svg"; + + if (playlistID) { + if (playlistID === "favorites") { + window.currentPlaylistID = playlistID; + window.playlist = favorites; + } else if (playlistID.startsWith("album:")) { + window.currentPlaylistID = playlistID; + window.playlist = albums[playlistID.substring(6)].tracks; + } else if (playlistID !== "keep") { + window.playlist = []; + } + } else { + window.currentPlaylistID = null; + window.playlist = []; + } + + window.playlist = shuffleArray(window.playlist); + window.playSong(window.playlist[0], "keep"); + } + + window.currentPlaylistID = null; + + window.playSong = async (id, playlistID) => { + playerDocument.getElementById("player-audio").pause(); + playerDocument.getElementById("player-audio").currentTime = 0; + + if (playlistID) { + if (playlistID === "favorites") { + window.currentPlaylistID = playlistID; + window.playlist = favorites; + window.currentPlaylistPosition = favorites.indexOf(id) ?? 0; + } else if (playlistID.startsWith("album:")) { + window.currentPlaylistID = playlistID; + window.playlist = albums[playlistID.substring(6)].tracks; + window.currentPlaylistPosition = albums[playlistID.substring(6)].tracks.indexOf(id) ?? 0; + } else if (playlistID !== "keep") { + window.playlist = [id]; + window.currentPlaylistPosition = 0; + } + } else { + window.currentPlaylistID = null; + window.playlist = [id]; + window.currentPlaylistPosition = 0; + } + + window.currentSong = songs[id]; + window.currentSongID = id; + updateDisplay(); + + if (!window.preloaded[id]) { + if (localStorage.getItem("data-saving") === "true") { + window.preloaded[id] = URL.createObjectURL(new Blob([await (await fetch("/assets/content/" + id + ".m4a")).arrayBuffer()], { type: "audio/mp4" })); + } else { + window.preloaded[id] = URL.createObjectURL(new Blob([await (await fetch("/assets/content/" + id + ".flac")).arrayBuffer()], { type: "audio/flac" })); + } + } + + cleanupPreload(); + preloadMore(); + + playerDocument.getElementById("player-audio").src = window.preloaded[id]; + playerDocument.getElementById("player-audio").play(); + + if (window.MistNative && localStorage.getItem("desktop-notification") === "true") { + window.MistNative.notification(currentSong, await (async function() { + let blob = await fetch("/assets/content/" + currentSongID + ".jpg").then(r => r.blob()); + return await new Promise(resolve => { + let reader = new FileReader(); + reader.onload = () => resolve(reader.result); + reader.readAsDataURL(blob); + }); + })()); + } + + document.getElementById("player").contentWindow.navigator.mediaSession.metadata = new MediaMetadata({ + title: currentSong.title, + artist: currentSong.artist, + album: currentSong.album, + artwork: [ + { src: location.protocol + "//" + location.host + '/assets/content/' + currentSongID + '.jpg', sizes: playerDocument.getElementById("album-art").naturalWidth + "x" + playerDocument.getElementById("album-art").naturalHeight, type: 'image/jpeg' }, + ] + }); + } + } + + async function preloadMore() { + for (let i = 1; i <= 10; i++) { + if (playlist[currentPlaylistPosition + i]) { + let id = playlist[currentPlaylistPosition + i]; + + if (!window.preloaded[id]) { + if (localStorage.getItem("data-saving") === "true") { + window.preloaded[id] = URL.createObjectURL(new Blob([await (await fetch("/assets/content/" + id + ".m4a")).arrayBuffer()], { type: "audio/mp4" })); + } else { + window.preloaded[id] = URL.createObjectURL(new Blob([await (await fetch("/assets/content/" + id + ".flac")).arrayBuffer()], { type: "audio/flac" })); + } + } + } + } + + cleanupPreload(); + } + + function cleanupPreload() { + let keys = Object.keys(window.preloaded).slice(-20); + + for (let key of Object.keys(window.preloaded)) { + if (!keys.includes(key)) { + URL.revokeObjectURL(window.preloaded[key]); + delete window.preloaded[key]; + } + } + } + </script> +</body> +</html>
\ No newline at end of file |