Files
com.xaymar.www/assets/site.js
T
Michael Fabian 'Xaymar' Dirks 441a8f0655 Prefer HTML over Liquid Templates
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.
2023-04-03 22:25:08 +02:00

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();
})();