441a8f0655
While Liquid is powerful, it is completely unreadable for many things, and actively makes documents harder to read. So instead of relying on it for everything, why not just use HTML5 for the page? The change reduces the parsing overhead for browser by about 30% through reducing the number of useless <div> elements. Additionally the CSS became easier to read as well.
435 lines
13 KiB
JavaScript
435 lines
13 KiB
JavaScript
'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("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();
|
|
})();
|