'use strict'; import("./HTMLMediaElement.mjs"); async function xmr_media_lazyload_perform(element) { let content = element.querySelector("noscript"); if (content) { element.innerHTML = content.innerText; } } async function xmr_media_lazyload_initialize() { // Figure out what to load. let elements = document.querySelectorAll(".block-media > [data-lazyload]"); let stack = Array.from(elements); console.debug("Lazily loading " + stack.length + " elements..."); // Add a lazyloading handler to all entries. for (let el of stack) { xmr_media_lazyload_perform(el); } } async function xmr_initialize_player(el) { let media = el.querySelector("video"); if (!media) el.querySelector("audio"); let play_pause = el.querySelector(".play"); { // Play/Pause play_pause.update = function() { if (media.paused) { play_pause.classList.remove("playing"); play_pause.dataset.symbol = "⏵"; play_pause.ariaLabel = "Play"; } else if (media.error || media.ended) { play_pause.classList.remove("playing"); play_pause.dataset.symbol = "⏹"; play_pause.ariaLabel = "Stopped"; } else { play_pause.classList.add("playing"); play_pause.dataset.symbol = "⏸"; play_pause.ariaLabel = "Pause"; } play_pause.innerText = play_pause.dataset.symbol; } play_pause.addEventListener("click", () => { if (media.paused) { media.play(); } else { media.pause(); } }); play_pause.addEventListener("keydown", (ev) => { if (ev.isComposing || ev.keyCode === 229) { // Ignore IME compositing. return; } if (["Space", "Enter"].includes(ev.code)) { play_pause.click(); ev.preventDefault(); ev.stopPropagation(); } }); media.addEventListener("play", () => { play_pause.update(); }); media.addEventListener("pause", () => { play_pause.update(); }); media.addEventListener("ended", () => { play_pause.update(); }); media.addEventListener("error", () => { play_pause.update(); }); play_pause.update(); } let playback_time = el.querySelector(".time"); { // Time playback_time.update = function() { function formatSeconds(seconds, with_ms, with_minutes, with_hour) { let result = []; if (isFinite(seconds) && !isNaN(seconds)) { let tainted_seconds = seconds % 60; let pure_seconds = Math.floor(seconds % 60); let tainted_minutes = ((seconds - pure_seconds) / 60); let pure_minutes = Math.floor(tainted_minutes % 60); let pure_hours = Math.floor((tainted_minutes - pure_minutes) / 60); result.unshift(`${pure_seconds.toString(10).padStart(2, '0')}`); if (with_ms) { result[0] = `${result[0]}.${Math.floor((tainted_seconds % 1) * 100).toString(10).padStart(2, '0')}` } if ((with_minutes === true) || ((with_minutes === undefined) && (pure_minutes > 0))) { result[0] = result[0].padStart(2, '0'); result.unshift(`${pure_minutes.toString(10)}`); } if ((with_hour === true) || ((with_hour === undefined) && (pure_hours > 0))) { result[0] = result[0].padStart(2, '0'); result.unshift(`${pure_hours.toString(10)}`); } } return result; } let duration = formatSeconds(media.duration, true); let current = formatSeconds(media.currentTime, true, duration.length >= 2, duration.length >= 3); for (let idx in duration) { current[idx].padStart(duration[idx].length, '0'); } if (duration.length > 0) { playback_time.innerText = `${current.join(':')} / ${duration.join(':')}`; } else { playback_time.innerText = `${current.join(':')}`; } } media.addEventListener("timeupdate", () => { playback_time.update(); }); media.addEventListener("durationupdate", () => { playback_progress.update(); }); media.addEventListener("loadedmetadata", () => { playback_time.update(); }); playback_time.update(); } let playback_progress = el.querySelector(".progress"); { // Progress playback_progress.update = function() { playback_progress.disabled = !media.seekable; playback_progress.min = 0; playback_progress.max = media.duration * 100; playback_progress.value = media.currentTime * 100; } playback_progress.addEventListener("input", () => { if (isFinite(media.duration)) { media.currentTime = parseInt(playback_progress.value, 10) / 100; } }); media.addEventListener("timeupdate", () => { playback_progress.update(); }); media.addEventListener("durationupdate", () => { playback_progress.update(); }); media.addEventListener("loadeddata", () => { playback_progress.update(); }); media.addEventListener("loadedmetadata", () => { playback_progress.update(); }); playback_progress.update(); } let audio_mute = el.querySelector(".mute"); { // Mute audio_mute.update = function() { if (media.muted) { audio_mute.classList.add("muted"); audio_mute.dataset.symbol = "🔇"; audio_mute.ariaLabel = "Unmute"; } else { audio_mute.classList.remove("muted"); if (media.volume > 0.5) { audio_mute.dataset.symbol = "🔊"; } else if (media.volume > 0.25) { audio_mute.dataset.symbol = "🔉"; } else { audio_mute.dataset.symbol = "🔈"; } audio_mute.ariaLabel = `Volume: ${(media.volume * 100).toString(10)}%`; } audio_mute.innerText = audio_mute.dataset.symbol; } audio_mute.addEventListener("click", () => { media.muted = !media.muted; }); audio_mute.addEventListener("keydown", (ev) => { if (ev.isComposing || ev.keyCode === 229) { // Ignore IME compositing. return; } if (["Space", "Enter"].includes(ev.code)) { audio_mute.click(); ev.preventDefault(); ev.stopPropagation(); } }); media.addEventListener("volumechange", () => { audio_mute.update(); }); audio_mute.update(); } let audio_volume = el.querySelector(".volume"); { // Volume audio_volume.min = 0; audio_volume.max = 2147483647; audio_volume.update = function() { let min = parseInt(audio_volume.min, 10); let max = parseInt(audio_volume.max, 10); let dlt = max - min; audio_volume.value = (media.logVolume * dlt) + min; audio_volume.ariaLabel = `Volume: ${(media.volume * 100).toString(10)}%`; if (media.muted) { audio_volume.classList.add("muted"); } else { audio_volume.classList.remove("muted"); } } audio_volume.addEventListener("input", () => { let min = parseInt(audio_volume.min, 10); let max = parseInt(audio_volume.max, 10); let val = parseInt(audio_volume.value, 10); let dlt = max - min; let vol = (val + min) / dlt; media.logVolume = vol; media.muted = (media.volume < 0.01); }); media.addEventListener("volumechange", () => { audio_volume.update(); }); audio_volume.update(); } let fullscreen = el.querySelector(".fullscreen"); { // Fullscreen fullscreen.update = function() { if (document.fullscreenElement) { fullscreen.dataset.symbol = "\u200F⛶"; } else { fullscreen.dataset.symbol = "⛶"; } fullscreen.innerText = fullscreen.dataset.symbol; }; fullscreen.addEventListener("click", () => { if (document.fullscreenElement) { document.exitFullscreen(); } else { el.requestFullscreen(); } }); fullscreen.addEventListener("keydown", (ev) => { if (ev.isComposing || ev.keyCode === 229) { // Ignore IME compositing. return; } if (["Space", "Enter"].includes(ev.code)) { fullscreen.click(); ev.preventDefault(); } }) el.addEventListener("fullscreenchange", () => { fullscreen.update(); }); fullscreen.update(); } let variant = el.querySelector(".variant"); { // Variants variant.update = function() { // Store current state and pause. let paused = media.paused; let muted = media.muted; let volume = media.volume; let time = media.currentTime; media.pause(); play_pause.update(); // Tell the media source to begin loading. media.src = variant.value; media.load(); media.currentTime = time; media.addEventListener("canplay", () => { media.currentTime = time; media.volume = volume; media.muted = muted; if (paused) { media.pause(); } else { media.play(); } play_pause.update(); }, { "once": true }) } variant.addEventListener("input", () => { variant.update(); }); variant.addEventListener("value", () => { variant.update(); }); // Variants - Add options from available sources. let sources = el.querySelectorAll("source"); for (let k of sources) { let option = document.createElement("option"); option.value = k.src; option.textContent = k.title; variant.appendChild(option); } variant.value = media.currentSrc; } { // Keyboard/Mouse Controls el.addEventListener("keydown", (ev) => { if (ev.isComposing || ev.keyCode === 229) { // Ignore IME compositing. return; } if (["f", "F"].includes(ev.key)) { fullscreen.click(); ev.preventDefault(); ev.stopPropagation(); } else if (["m", "M"].includes(ev.key)) { audio_mute.click(); ev.preventDefault(); ev.stopPropagation(); } else if (ev.code === "Space") { play_pause.click(); ev.preventDefault(); ev.stopPropagation(); } else if (ev.code === "Home") { media.currentTime = 0; ev.preventDefault(); ev.stopPropagation(); } else if (ev.code === "ArrowLeft") { media.currentTime -= 5; ev.preventDefault(); ev.stopPropagation(); } else if (ev.code === "Comma") { media.currentTime -= .1; ev.preventDefault(); ev.stopPropagation(); } else if (ev.code === "Period") { media.currentTime += .1; ev.preventDefault(); ev.stopPropagation(); } else if (ev.code === "ArrowRight") { media.currentTime += 5; ev.preventDefault(); ev.stopPropagation(); } else if (ev.code === "End") { media.currentTime = media.duration; ev.preventDefault(); ev.stopPropagation(); } else if (ev.code === "ArrowUp") { media.logVolume = Math.max(0, Math.min(media.logVolume + 0.05, 1.0)); ev.preventDefault(); ev.stopPropagation(); } else if (ev.code === "ArrowDown") { media.logVolume = Math.max(0, Math.min(media.logVolume - 0.05, 1.0)); ev.preventDefault(); ev.stopPropagation(); } else if (["p", "P"].includes(ev.key)) { media.preservesPitch = !media.preservesPitch; ev.preventDefault(); ev.stopPropagation(); } else if (ev.code === "PageDown") { media.playbackRate = Math.max(0.25, Math.min(media.playbackRate - 0.05, 4.0)); ev.preventDefault(); ev.stopPropagation(); } else if (ev.code === "PageUp") { media.playbackRate = Math.max(0.25, Math.min(media.playbackRate + 0.05, 4.0)); ev.preventDefault(); ev.stopPropagation(); } else if (["r", "R"].includes(ev.key)) { media.playbackRate = 1.0; ev.preventDefault(); ev.stopPropagation(); } }); media.addEventListener("click", () => { play_pause.click(); }); media.addEventListener("dblclick", () => { fullscreen.click(); }) } { // Hide/Show controls el.showOverlay = function() { el.cancelHideOverlay(); el.classList.remove("hide"); } el.hideOverlay = function() { if (!media.paused && !media.ended && !media.error) { el.classList.add("hide"); } } el.delayHideOverlay = function() { el.cancelHideOverlay(); el.timer = setTimeout(() => { el.hideOverlay(); }, 2500); } el.cancelHideOverlay = function() { if (el.timer) { clearTimeout(el.timer); } } el.addEventListener("mousemove", () => { el.showOverlay(); if (!media.paused && !media.ended && !media.error) { el.delayHideOverlay(); } }); el.hideOverlay(); } { // Show/Hide Audio controls without audio. function checkAudioPresence() { let hasAudio = Boolean(media.mozHasAudio) || Boolean(media.webkitAudioDecodedByteCount) || Boolean(media.audioTracks && media.audioTracks.length); audio_mute.style.display = hasAudio ? "" : "none"; audio_volume.style.display = hasAudio ? "" : "none"; } media.addEventListener("loadedmetadata", () => { checkAudioPresence() }); media.addEventListener("loadeddata", () => { checkAudioPresence() }); checkAudioPresence(); } // Signal the browser to try and load some information. media.load(); } async function xmr_initialize_players() { // Figure out what to initialize. let elements = document.querySelectorAll(".block-media.player"); let stack = Array.from(elements); console.debug("Initializing " + stack.length + " players..."); // Add a lazyloading handler to all entries. for (let el of stack) { xmr_initialize_player(el); } } (function() { 'use strict'; // Support for Mobile Devices document.querySelector("#navigation-toggle").addEventListener("click", (ev) => { document.querySelector("#header #navigation").classList.toggle("open"); }); // Lazily load Media xmr_media_lazyload_initialize(); // Initialize media player. xmr_initialize_players(); })();