363 lines
10 KiB
JavaScript
363 lines
10 KiB
JavaScript
document.addEventListener("DOMContentLoaded", () => {
|
|
// init
|
|
const introduction = document.querySelector("#introduction");
|
|
if (introduction) playIntroduction();
|
|
|
|
initNav();
|
|
initNotes();
|
|
styleCallouts();
|
|
linkLayersScrolls();
|
|
|
|
let currentUrl = location.href;
|
|
|
|
window.onpopstate = function () {
|
|
let target = null;
|
|
const currentLayerId = document.querySelector(".current").id;
|
|
|
|
if (currentLayerId === "first-level") {
|
|
window.location.href = window.location.href;
|
|
} else if (currentLayerId === "second-level") {
|
|
history.pushState(null, document.title, currentUrl);
|
|
target = document.querySelector("#first-level .layer-btn");
|
|
const event = {
|
|
preventDefault: () => {},
|
|
stopPropagation: () => {},
|
|
target: target,
|
|
};
|
|
go(event);
|
|
} else if (currentLayerId === "third-level") {
|
|
history.pushState(null, document.title, currentUrl);
|
|
target = document.querySelector("#second-level .layer-btn");
|
|
const event = {
|
|
preventDefault: () => {},
|
|
stopPropagation: () => {},
|
|
target: target,
|
|
};
|
|
go(event);
|
|
}
|
|
};
|
|
|
|
const collapsableSectionBtns = document.querySelectorAll(
|
|
".collapsable__button"
|
|
);
|
|
collapsableSectionBtns.forEach((btn) => {
|
|
btn.addEventListener("click", () => {
|
|
const section = btn.parentNode;
|
|
section.classList.toggle("open");
|
|
});
|
|
});
|
|
|
|
animateCursor();
|
|
|
|
// functions
|
|
let ticking = false;
|
|
|
|
function linkLayersScrolls() {
|
|
const thirdLayer = document.querySelector("#third-level");
|
|
let secondLayer = document.querySelector("#second-level");
|
|
thirdLayer.addEventListener("scroll", () => {
|
|
if (!ticking) {
|
|
window.requestAnimationFrame(() => {
|
|
scrollLayer(secondLayer, thirdLayer.scrollTop);
|
|
ticking = false;
|
|
});
|
|
ticking = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
function scrollLayer(secondLayer, scrollTop) {
|
|
if (window.innerWidth > 650) return;
|
|
const parallaxFactor = 0.5;
|
|
if (secondLayer.scrollTop <= 40) {
|
|
let newScrollTop = scrollTop * parallaxFactor;
|
|
newScrollTop = newScrollTop > 40 ? 40 : newScrollTop;
|
|
secondLayer.scrollTop = newScrollTop;
|
|
}
|
|
}
|
|
|
|
function playIntroduction() {
|
|
const parts = introduction.querySelectorAll("span");
|
|
const step = 800;
|
|
parts.forEach((part, index) => {
|
|
setTimeout(() => {
|
|
part.classList.remove("hide");
|
|
}, step + step * index);
|
|
});
|
|
setTimeout(() => {
|
|
introduction.classList.add("hide");
|
|
}, parts.length + 5 * step);
|
|
}
|
|
|
|
function formatSize(size) {
|
|
if (size < 1_000) {
|
|
return {
|
|
size: size,
|
|
unit: " o",
|
|
};
|
|
} else if (size < 1_000_000) {
|
|
return {
|
|
size: size / 1_000,
|
|
unit: " Ko",
|
|
};
|
|
} else if (size < 1_000_000_000) {
|
|
return {
|
|
size: size / 1_000_000,
|
|
unit: " Mo",
|
|
};
|
|
} else {
|
|
return {
|
|
size: size / 1_000_000_000,
|
|
unit: " Go",
|
|
};
|
|
}
|
|
}
|
|
|
|
function initNotes() {
|
|
const notes = document.querySelectorAll(".note");
|
|
notes.forEach((note) => {
|
|
note.addEventListener("click", () => {
|
|
note.classList.toggle("open");
|
|
note.querySelector(".details").classList.toggle("hide");
|
|
});
|
|
});
|
|
}
|
|
|
|
function animateCursor() {
|
|
const cursor = document.getElementById("cursor");
|
|
|
|
let animationFrameId;
|
|
|
|
document.addEventListener("mousemove", (e) => {
|
|
if (window.innerWidth < 640) return;
|
|
const hoveredLayer = e.target.closest(".layer");
|
|
if (hoveredLayer.id === "first-level") {
|
|
cursor.classList.add("light");
|
|
} else if (!e.target.closest(".collapsable__button")) {
|
|
cursor.classList.remove("light");
|
|
}
|
|
isCursorVisible = true;
|
|
cursor.style.display = "flex";
|
|
document.body.style.cursor = "none";
|
|
|
|
const x = e.clientX - 16;
|
|
const y = e.clientY - 16 + window.scrollY;
|
|
|
|
cancelAnimationFrame(animationFrameId);
|
|
|
|
animationFrameId = requestAnimationFrame(() => {
|
|
cursor.style.left = x + "px";
|
|
cursor.style.top = y + "px";
|
|
});
|
|
});
|
|
}
|
|
|
|
function initNav() {
|
|
listenLinks();
|
|
showTargetPageSize();
|
|
listenCollapsableSections();
|
|
}
|
|
|
|
function listenLinks() {
|
|
const internalLinks = document.querySelectorAll("a.internal-link");
|
|
internalLinks.forEach((btn) => {
|
|
btn.addEventListener("click", go);
|
|
});
|
|
|
|
const imageLinks = document.querySelectorAll("figure a");
|
|
|
|
imageLinks.forEach((link) => {
|
|
const cursorText = document.querySelector("#cursor #text");
|
|
link.addEventListener("mouseenter", () => {
|
|
const href = new URL(link.href);
|
|
cursorText.style.display = "flex";
|
|
cursorText.textContent = "ouvrir " + href.host;
|
|
});
|
|
link.addEventListener("mouseleave", () => {
|
|
cursorText.style.display = "";
|
|
cursorText.textContent = "";
|
|
});
|
|
});
|
|
}
|
|
|
|
function removeListeners() {
|
|
const internalLinks = document.querySelectorAll("a.internal-link");
|
|
internalLinks.forEach((btn) => {
|
|
btn.removeEventListener("click", go);
|
|
});
|
|
}
|
|
|
|
function removeTargetPageListeners() {
|
|
const internalLinks = document.querySelectorAll("a.internal-link");
|
|
internalLinks.forEach((link) => {
|
|
link.removeEventListener("mouseenter", handleMouseEnter);
|
|
link.removeEventListener("mouseleave", handleMouseLeave);
|
|
});
|
|
}
|
|
|
|
function handleMouseEnter() {
|
|
const cursorText = document.querySelector("#cursor #text");
|
|
if (this.href === window.location.href) return;
|
|
cursorText.style.display = "flex";
|
|
cursorText.textContent = this.dataset.size;
|
|
}
|
|
|
|
function handleMouseLeave() {
|
|
const cursorText = document.querySelector("#cursor #text");
|
|
cursorText.style.display = "";
|
|
cursorText.textContent = "";
|
|
}
|
|
|
|
function toggleVisibility(element, condition) {
|
|
element.classList.remove("unvisible", condition);
|
|
}
|
|
|
|
function moveBackward(wrapper, page) {
|
|
page.content
|
|
.querySelector(".current .layer-btn")
|
|
.classList.remove("unvisible");
|
|
|
|
wrapper.innerHTML = page.content.innerHTML;
|
|
wrapper.classList.add("current");
|
|
|
|
setTimeout(() => {
|
|
wrapper.querySelector(".current .layer-btn").classList.add("unvisible");
|
|
}, 50);
|
|
}
|
|
|
|
function moveForward(originWrapper, page) {
|
|
const isThirdLevel = originWrapper.id === "third-level";
|
|
const targetWrapper = isThirdLevel
|
|
? document.querySelector("#second-level")
|
|
: originWrapper.nextElementSibling;
|
|
|
|
originWrapper.classList.remove("current");
|
|
targetWrapper.classList.add("current");
|
|
|
|
const emptys = page.content.querySelectorAll(".empty");
|
|
const current = document.querySelector(".current");
|
|
|
|
if (current.id === "second-level" && emptys.length === 3) {
|
|
const firstEmpty = page.content.querySelector(".empty");
|
|
firstEmpty.parentNode.removeChild(firstEmpty);
|
|
}
|
|
|
|
targetWrapper.innerHTML = page.content.innerHTML;
|
|
targetWrapper.classList.remove("out-screen");
|
|
}
|
|
|
|
function go(event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
const btn = event.target.closest("a");
|
|
const wrapper = btn.closest(".layer");
|
|
const targetUrl = btn.href;
|
|
|
|
if (targetUrl === window.location.href) return;
|
|
|
|
removeListeners();
|
|
removeTargetPageListeners();
|
|
|
|
fetch(targetUrl)
|
|
.then((res) => res.text())
|
|
.then((htmlString) => {
|
|
const page = getPage(htmlString);
|
|
history.pushState(null, page.title, targetUrl);
|
|
|
|
let timer = 0;
|
|
|
|
const currentLevel = document.querySelector(".current");
|
|
if (currentLevel.scrollTop > 0) {
|
|
document.querySelector(".current").scrollTo({
|
|
top: 0,
|
|
behavior: "smooth",
|
|
});
|
|
timer = 500;
|
|
}
|
|
|
|
setTimeout(() => {
|
|
if (wrapper.classList.contains("current")) {
|
|
moveForward(wrapper, page);
|
|
} else {
|
|
moveBackward(wrapper, page);
|
|
}
|
|
|
|
document.querySelector("title").textContent = `${page.title}`;
|
|
|
|
document.querySelectorAll(`.current ~ .layer`).forEach((layer) => {
|
|
layer.classList.add("out-screen");
|
|
});
|
|
|
|
const layerBtnsToShow = document.querySelectorAll(
|
|
".layer-btn:not(.current .layer-btn)"
|
|
);
|
|
|
|
layerBtnsToShow.forEach((layerBtn) => {
|
|
toggleVisibility(layerBtn, window.innerWidth < 640);
|
|
});
|
|
|
|
listenLinks();
|
|
showTargetPageSize();
|
|
styleCallouts();
|
|
initNotes();
|
|
linkLayersScrolls();
|
|
currentUrl = window.location.href;
|
|
}, timer);
|
|
})
|
|
.catch((err) => {
|
|
console.error("Fetch Error:", err);
|
|
});
|
|
}
|
|
|
|
function showTargetPageSize() {
|
|
const internalLinks = document.querySelectorAll("a.internal-link");
|
|
internalLinks.forEach((link) => {
|
|
link.addEventListener("mouseenter", handleMouseEnter);
|
|
link.addEventListener("mouseleave", handleMouseLeave);
|
|
});
|
|
}
|
|
|
|
function listenCollapsableSections() {
|
|
const collapsableSections = document.querySelectorAll(
|
|
".collapsable__button"
|
|
);
|
|
|
|
const cursorText = document.querySelector("#cursor #text");
|
|
collapsableSections.forEach((section) => {
|
|
section.addEventListener("mouseenter", () => {
|
|
cursorText.style.display = "flex";
|
|
cursorText.textContent = "ouvrir/fermer";
|
|
});
|
|
section.addEventListener("mouseleave", () => {
|
|
cursorText.style.display = "";
|
|
cursorText.textContent = "";
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
function getPage(htmlString) {
|
|
const parser = new DOMParser();
|
|
const fullPage = parser.parseFromString(htmlString, "text/html");
|
|
const title = fullPage.querySelector("title").textContent;
|
|
const content = fullPage.querySelector(".layer.current");
|
|
|
|
return {
|
|
title: title,
|
|
content: content,
|
|
};
|
|
}
|
|
|
|
function getUrlLastPart(url) {
|
|
const parts = url.split("/");
|
|
const lastPart = parts[parts.length - 1];
|
|
return lastPart;
|
|
}
|
|
|
|
function styleCallouts() {
|
|
const callouts = document.querySelectorAll(".callout");
|
|
callouts.forEach((callout) => {
|
|
if (!callout.closest("p")) return;
|
|
callout.closest("p").classList.add("callout");
|
|
});
|
|
}
|