201 lines
5.6 KiB
JavaScript
201 lines
5.6 KiB
JavaScript
(() => {
|
|
const assetTokenStorageKey = "chesscubing-arena-asset-token";
|
|
let viewportStarted = false;
|
|
let audioContext = null;
|
|
|
|
function syncViewportHeight() {
|
|
const visibleHeight = window.visualViewport?.height ?? window.innerHeight;
|
|
const viewportTopOffset = window.visualViewport?.offsetTop ?? 0;
|
|
const viewportHeight = Math.max(
|
|
visibleHeight + viewportTopOffset,
|
|
window.innerHeight,
|
|
document.documentElement.clientHeight,
|
|
);
|
|
|
|
document.documentElement.style.setProperty("--app-visible-height", `${Math.round(visibleHeight)}px`);
|
|
document.documentElement.style.setProperty("--app-viewport-top", `${Math.round(viewportTopOffset)}px`);
|
|
document.documentElement.style.setProperty("--app-viewport-height", `${Math.round(viewportHeight)}px`);
|
|
}
|
|
|
|
function startViewport() {
|
|
if (viewportStarted) {
|
|
return;
|
|
}
|
|
|
|
viewportStarted = true;
|
|
syncViewportHeight();
|
|
window.addEventListener("load", syncViewportHeight);
|
|
window.addEventListener("resize", syncViewportHeight);
|
|
window.addEventListener("scroll", syncViewportHeight, { passive: true });
|
|
window.addEventListener("pageshow", syncViewportHeight);
|
|
window.addEventListener("orientationchange", syncViewportHeight);
|
|
window.visualViewport?.addEventListener("resize", syncViewportHeight);
|
|
window.visualViewport?.addEventListener("scroll", syncViewportHeight);
|
|
window.setTimeout(syncViewportHeight, 0);
|
|
window.setTimeout(syncViewportHeight, 150);
|
|
window.setTimeout(syncViewportHeight, 400);
|
|
window.requestAnimationFrame(() => window.requestAnimationFrame(syncViewportHeight));
|
|
}
|
|
|
|
function getAudioContext() {
|
|
const AudioContextClass = window.AudioContext || window.webkitAudioContext;
|
|
if (!AudioContextClass) {
|
|
return null;
|
|
}
|
|
|
|
if (!audioContext) {
|
|
try {
|
|
audioContext = new AudioContextClass();
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return audioContext;
|
|
}
|
|
|
|
function primeAudio() {
|
|
const context = getAudioContext();
|
|
if (!context || context.state === "running") {
|
|
return;
|
|
}
|
|
|
|
context.resume().catch(() => undefined);
|
|
}
|
|
|
|
function playCubePhaseAlert() {
|
|
primeAudio();
|
|
const context = getAudioContext();
|
|
if (!context || context.state !== "running") {
|
|
return false;
|
|
}
|
|
|
|
const pattern = [
|
|
{ frequency: 740, offset: 0, duration: 0.12, gain: 0.035 },
|
|
{ frequency: 988, offset: 0.18, duration: 0.12, gain: 0.04 },
|
|
{ frequency: 1318, offset: 0.36, duration: 0.2, gain: 0.05 },
|
|
];
|
|
const startAt = context.currentTime + 0.02;
|
|
|
|
pattern.forEach(({ frequency, offset, duration, gain }) => {
|
|
const oscillator = context.createOscillator();
|
|
const envelope = context.createGain();
|
|
const toneStartAt = startAt + offset;
|
|
|
|
oscillator.type = "triangle";
|
|
oscillator.frequency.setValueAtTime(frequency, toneStartAt);
|
|
envelope.gain.setValueAtTime(0.0001, toneStartAt);
|
|
envelope.gain.exponentialRampToValueAtTime(gain, toneStartAt + 0.02);
|
|
envelope.gain.exponentialRampToValueAtTime(0.0001, toneStartAt + duration);
|
|
|
|
oscillator.connect(envelope);
|
|
envelope.connect(context.destination);
|
|
oscillator.start(toneStartAt);
|
|
oscillator.stop(toneStartAt + duration + 0.03);
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
window.chesscubingPage = {
|
|
setBodyState(page, bodyClass) {
|
|
if (page) {
|
|
document.body.dataset.page = page;
|
|
} else {
|
|
delete document.body.dataset.page;
|
|
}
|
|
|
|
document.body.className = bodyClass || "";
|
|
},
|
|
};
|
|
|
|
window.chesscubingViewport = {
|
|
start: startViewport,
|
|
};
|
|
|
|
window.chesscubingStorage = {
|
|
getMatchState(storageKey, windowNameKey) {
|
|
try {
|
|
const raw = window.localStorage.getItem(storageKey);
|
|
if (raw) {
|
|
return raw;
|
|
}
|
|
} catch {
|
|
}
|
|
|
|
try {
|
|
if (!window.name || !window.name.startsWith(windowNameKey)) {
|
|
return null;
|
|
}
|
|
|
|
return window.name.slice(windowNameKey.length);
|
|
} catch {
|
|
return null;
|
|
}
|
|
},
|
|
|
|
setMatchState(storageKey, windowNameKey, value) {
|
|
try {
|
|
window.name = `${windowNameKey}${value}`;
|
|
} catch {
|
|
}
|
|
|
|
try {
|
|
window.localStorage.setItem(storageKey, value);
|
|
} catch {
|
|
}
|
|
},
|
|
|
|
clearMatchState(storageKey) {
|
|
try {
|
|
window.localStorage.removeItem(storageKey);
|
|
} catch {
|
|
}
|
|
|
|
try {
|
|
window.name = "";
|
|
} catch {
|
|
}
|
|
},
|
|
};
|
|
|
|
window.chesscubingAudio = {
|
|
prime: primeAudio,
|
|
playCubePhaseAlert,
|
|
};
|
|
|
|
window.chesscubingBrowser = {
|
|
async forceRefresh(path) {
|
|
const refreshToken = `${Date.now()}`;
|
|
|
|
try {
|
|
window.sessionStorage.setItem(assetTokenStorageKey, refreshToken);
|
|
} catch {
|
|
}
|
|
|
|
if ("caches" in window) {
|
|
try {
|
|
const cacheKeys = await window.caches.keys();
|
|
await Promise.all(cacheKeys.map((cacheKey) => window.caches.delete(cacheKey)));
|
|
} catch {
|
|
}
|
|
}
|
|
|
|
if ("serviceWorker" in navigator) {
|
|
try {
|
|
const registrations = await navigator.serviceWorker.getRegistrations();
|
|
await Promise.all(registrations.map((registration) => registration.update().catch(() => undefined)));
|
|
await Promise.all(registrations.map((registration) => registration.unregister().catch(() => undefined)));
|
|
} catch {
|
|
}
|
|
}
|
|
|
|
const targetUrl = new URL(path, window.location.href);
|
|
targetUrl.searchParams.set("refresh", refreshToken);
|
|
window.location.replace(targetUrl.toString());
|
|
},
|
|
};
|
|
|
|
startViewport();
|
|
})();
|