-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.js
More file actions
144 lines (125 loc) · 4.7 KB
/
main.js
File metadata and controls
144 lines (125 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
document.addEventListener("DOMContentLoaded", () => {
// --- Navigation & Mobile Menu Logic ---
const navBtns = document.querySelectorAll('.nav-btn');
const pages = document.querySelectorAll('.page-section');
const hamburgerBtn = document.getElementById('hamburger-btn');
const navLinksMenu = document.getElementById('nav-links');
const brandLogo = document.getElementById('brand-logo');
// Toggle mobile menu
hamburgerBtn.addEventListener('click', () => {
navLinksMenu.classList.toggle('show');
});
// Make the logo act like the "Home" button
brandLogo.addEventListener('click', () => {
const homeBtn = document.querySelector('.nav-btn[data-target="home"]');
if (homeBtn) homeBtn.click();
});
// Switch pages
navBtns.forEach(btn => {
btn.addEventListener('click', () => {
// Remove active from all
navBtns.forEach(b => b.classList.remove('active'));
pages.forEach(p => p.classList.remove('active'));
// Add active to clicked
btn.classList.add('active');
const targetId = btn.getAttribute('data-target');
document.getElementById(targetId).classList.add('active');
// Close mobile menu if open after clicking a link
navLinksMenu.classList.remove('show');
});
});
// --- Typewriter Logic ---
const typedText = document.getElementById("typed-text");
const word = "mobx";
const TYPE_SPEED = 120;
const ERASE_SPEED = 80;
const TYPEWRITER_CYCLE_MS = 5000;
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function runTypewriterLoop() {
while (true) {
typedText.textContent = "";
for (const char of word) {
typedText.textContent += char;
await sleep(TYPE_SPEED);
}
const usedMs = word.length * TYPE_SPEED + word.length * ERASE_SPEED;
const holdMs = Math.max(300, TYPEWRITER_CYCLE_MS - usedMs);
await sleep(holdMs);
while (typedText.textContent.length > 0) {
typedText.textContent = typedText.textContent.slice(0, -1);
await sleep(ERASE_SPEED);
}
await sleep(120);
}
}
// --- Capybara Logic ---
const capyImg = document.getElementById("capy-img");
const factEl = document.getElementById("capy-fact");
const button = document.getElementById("new-capy-btn");
const imageWrap = document.getElementById("image-wrap");
function setLoadingUI(isLoading, imageReady = false) {
if (isLoading) {
imageWrap.classList.add("is-loading");
imageWrap.classList.remove("is-loaded");
} else {
imageWrap.classList.remove("is-loading");
if (imageReady) imageWrap.classList.add("is-loaded");
}
imageWrap.setAttribute("aria-busy", String(isLoading));
button.disabled = isLoading;
}
function preloadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(url);
img.onerror = reject;
img.src = url;
});
}
async function fetchRandomCapybara() {
const res = await fetch("https://api.capy.lol/v1/capybara?json=true", { cache: "no-store" });
if (!res.ok) throw new Error(`Image HTTP ${res.status}`);
const json = await res.json();
const data = json?.data;
if (!data?.url) throw new Error("Missing image URL");
const imageUrl = data.url.replace(/^http:\/\//i, "https://");
await preloadImage(imageUrl);
capyImg.src = imageUrl;
capyImg.alt = data.alt || "Random capybara";
}
async function fetchCapyFact() {
const res = await fetch("https://api.capy.lol/v1/fact", { cache: "no-store" });
if (!res.ok) throw new Error(`Fact HTTP ${res.status}`);
const json = await res.json();
const fact = json?.data?.fact;
if (!fact) throw new Error("Missing fact text");
factEl.textContent = fact;
}
async function loadContent() {
let imageLoaded = false;
setLoadingUI(true);
factEl.textContent = "Loading a capybara fact...";
try {
const [imageResult, factResult] = await Promise.allSettled([
fetchRandomCapybara(),
fetchCapyFact(),
]);
if (imageResult.status === "rejected") {
console.error(imageResult.reason);
capyImg.alt = "Failed to load capybara image";
} else {
imageLoaded = true;
}
if (factResult.status === "rejected") {
console.error(factResult.reason);
factEl.textContent = "Could not load a capybara fact this time.";
}
} finally {
setLoadingUI(false, imageLoaded);
}
}
// Initialize active functions
button.addEventListener("click", loadContent);
runTypewriterLoop();
loadContent();
});