TDD: 1, Me: 0
This commit is contained in:
260
app/ui/static/script.js
Normal file
260
app/ui/static/script.js
Normal file
@@ -0,0 +1,260 @@
|
||||
// Disable GSAP's use of requestAnimationFrame
|
||||
gsap.ticker.fps(60);
|
||||
gsap.ticker.lagSmoothing(0);
|
||||
|
||||
// Add an event listener to call showLoadingSpinner when the DOM is fully loaded
|
||||
// document.addEventListener('DOMContentLoaded', function() { showLoadingSpinner(); });
|
||||
|
||||
function select(selector) {
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
|
||||
function showLoadingSpinner() {
|
||||
const spinner = select("#loading-spinner");
|
||||
spinner.style.display = "block";
|
||||
}
|
||||
|
||||
function hideLoadingSpinner() {
|
||||
const spinner = select("#loading-spinner");
|
||||
spinner.style.display = "none";
|
||||
}
|
||||
|
||||
document.addEventListener("click", debounce((e) => {
|
||||
if (e.target.closest(`[data-action="openModal"]`)) {
|
||||
const device = e.target.dataset.device;
|
||||
const action = e.target.dataset.actionType;
|
||||
let url = `/${action}/${device}`;
|
||||
if (action === "wol") {
|
||||
fetchKey().then((key) => {
|
||||
url += `?key=${key}`;
|
||||
showLoadingSpinner();
|
||||
openModal(url);
|
||||
}).catch(error => {
|
||||
console.error('Error fetching key:', error);
|
||||
});
|
||||
} else {
|
||||
showLoadingSpinner();
|
||||
openModal(url);
|
||||
}
|
||||
}
|
||||
if (
|
||||
e.target.closest(`[data-action="closeModal"]`) ||
|
||||
e.target.classList.contains("modal-wrapper")
|
||||
) {
|
||||
closeModal();
|
||||
}
|
||||
}, 300));
|
||||
|
||||
let countdown;
|
||||
|
||||
async function fetchKey() {
|
||||
try {
|
||||
const response = await fetch('/get_key');
|
||||
const data = await response.json();
|
||||
return data.key;
|
||||
} catch (error) {
|
||||
console.error('Error fetching key:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function openModal(url) {
|
||||
closeExistingModal().then(() => {
|
||||
fetch(url)
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
hideLoadingSpinner();
|
||||
const modal = `
|
||||
<div class="modal-wrapper">
|
||||
<div class="modal-container">
|
||||
<button class="modal-close" data-action="closeModal">
|
||||
<svg viewBox="0 0 20 20" width="16" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M 2 2 L 18 18" stroke-width="3" fill="transparent"></path>
|
||||
<path d="M 18 2 L 2 18" stroke-width="3" fill="transparent"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<h3 class="modal-heading">Server says</h3>
|
||||
<br>
|
||||
${html}
|
||||
<div class="modal-timer" id="modal-timer"></div>
|
||||
</div>
|
||||
</div>`;
|
||||
select("body").insertAdjacentHTML("beforeend", modal);
|
||||
const wrapper = select(".modal-wrapper");
|
||||
wrapper.style.display = "flex";
|
||||
select("body").style.overflow = "hidden";
|
||||
setTimeout(() => {
|
||||
wrapper.style.opacity = 1;
|
||||
wrapper.classList.add("show");
|
||||
startCountdown();
|
||||
}, 100);
|
||||
}).catch(error => {
|
||||
hideLoadingSpinner();
|
||||
console.error('Error fetching content:', error)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
const wrapper = document.querySelector(".modal-wrapper");
|
||||
return new Promise((resolve) => {
|
||||
if (wrapper) {
|
||||
const container = document.querySelector(".modal-container");
|
||||
if (container) {
|
||||
container.style.transform = 'scale(0)';
|
||||
}
|
||||
wrapper.style.opacity = 0;
|
||||
setTimeout(() => {
|
||||
if (wrapper.parentNode) {
|
||||
wrapper.remove();
|
||||
}
|
||||
select("body").style.overflow = "";
|
||||
clearTimeout(countdown);
|
||||
resolve();
|
||||
}, 500);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function closeExistingModal() {
|
||||
const existingWrapper = document.querySelector(".modal-wrapper");
|
||||
return new Promise((resolve) => {
|
||||
if (existingWrapper) {
|
||||
existingWrapper.style.opacity = 0;
|
||||
setTimeout(() => {
|
||||
if (existingWrapper.parentNode) {
|
||||
existingWrapper.remove();
|
||||
}
|
||||
select("body").style.overflow = ""; // Reset body overflow
|
||||
clearTimeout(countdown); // Clear any existing countdown
|
||||
resolve();
|
||||
}, 500);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function startCountdown() {
|
||||
const timer = document.getElementById('modal-timer');
|
||||
if (timer) {
|
||||
timer.classList.add('timer-animation');
|
||||
countdown = setTimeout(() => {
|
||||
closeModal();
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
/* Debounce Function */
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function(...args) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(this, args), wait);
|
||||
};
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
|
||||
|
||||
document.querySelector(".preloader").style.display = 'flex';
|
||||
|
||||
const loadingText = new SplitType(".loading-text.initial", { types: "chars" });
|
||||
const completeText = new SplitType(".loading-text.complete", { types: "chars" });
|
||||
const titleText = new SplitType(".content h1", { types: "chars" });
|
||||
const paragraphText = new SplitType(".content p", { types: "chars" });
|
||||
|
||||
gsap.set(".loading-text.complete", { y: "100%" });
|
||||
gsap.set(loadingText.chars, { opacity: 0, y: 100 });
|
||||
gsap.set(completeText.chars, { opacity: 0, y: 100 });
|
||||
|
||||
gsap.to(loadingText.chars, {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
duration: 0.5,
|
||||
stagger: 0.05,
|
||||
ease: "power2.out"
|
||||
});
|
||||
|
||||
|
||||
const colorStages = [
|
||||
{ bg: "rgb(60, 66, 55)", text: "rgb(230, 225, 215)" },
|
||||
{ bg: "rgb(200, 180, 160)", text: "rgb(60, 66, 55)" },
|
||||
{ bg: "rgb(230, 225, 215)", text: "rgb(60, 66, 55)" },
|
||||
{ bg: "rgb(100, 110, 90)", text: "rgb(230, 225, 215)" }
|
||||
];
|
||||
|
||||
function updateColors(progress) {
|
||||
const stage = Math.floor(progress / 25);
|
||||
if (stage < colorStages.length) {
|
||||
document.querySelector(".preloader").style.backgroundColor = colorStages[stage].bg;
|
||||
document.querySelector(".progress-bar").style.backgroundColor = colorStages[stage].text;
|
||||
document.querySelectorAll(".loading-text .char, .percentage").forEach((el) => {
|
||||
el.style.color = colorStages[stage].text;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
tl.to(".progress-bar", {
|
||||
width: "100%",
|
||||
duration: 5,
|
||||
ease: "power1.inOut",
|
||||
onUpdate: function () {
|
||||
const progress = Math.round(this.progress() * 100);
|
||||
document.querySelector(".percentage").textContent = progress;
|
||||
updateColors(progress);
|
||||
}
|
||||
}).to(".loading-text.initial", {
|
||||
y: "-100%",
|
||||
duration: 0.5,
|
||||
ease: "power2.inOut"
|
||||
}).to(".loading-text.complete", {
|
||||
y: "0%",
|
||||
duration: 0.5,
|
||||
ease: "power2.inOut"
|
||||
}, "<" ).to(completeText.chars, {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
duration: 0.3,
|
||||
stagger: 0.03,
|
||||
ease: "power2.out"
|
||||
}, "<0.2" ).to(".preloader", {
|
||||
y: "-100vh",
|
||||
duration: 1,
|
||||
ease: "power2.inOut",
|
||||
delay: 0.8
|
||||
}).set(".content", {
|
||||
visibility: "visible"
|
||||
}, "-=1").to([titleText.chars, paragraphText.chars], {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
duration: 1,
|
||||
stagger: 0.02,
|
||||
ease: "power4.out"
|
||||
}, "-=0.5").set(".preloader", {
|
||||
display: "none" });
|
||||
|
||||
let isFirstLoad = !localStorage.getItem('firstLoadDone');
|
||||
if (!isFirstLoad) {
|
||||
const tl = gsap.timeline();
|
||||
tl.set(".preloader", {
|
||||
display: "none"
|
||||
}).set(".content", {
|
||||
visibility: "visible"
|
||||
}, "-=1").to([titleText.chars, paragraphText.chars], {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
duration: 2,
|
||||
stagger: 0.04,
|
||||
ease: "power2.out"
|
||||
}, "=0.5")
|
||||
} else {
|
||||
document.querySelector('.preloader').style.display = 'flex';
|
||||
tl.play(); // Start the GSAP timeline
|
||||
localStorage.setItem('firstLoadDone', 'true');
|
||||
}
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user