forked from FWS/TEMPLATE-Web-site-one-page
Compare commits
11 Commits
e315b511ee
...
6a7566e149
| Author | SHA1 | Date | |
|---|---|---|---|
|
6a7566e149
|
|||
|
ec2c75de33
|
|||
|
41c2c58dba
|
|||
|
07f232b70b
|
|||
|
06cef39749
|
|||
|
3b844a3718
|
|||
|
07b54a7f5d
|
|||
|
bb72082230
|
|||
|
4a7df3e5b7
|
|||
|
5ccc51e8e5
|
|||
|
bca5626096
|
@@ -0,0 +1,212 @@
|
||||
(() => {
|
||||
const OWNER = "You-re-like-Windows-a-bitch";
|
||||
const REPO = "ISO";
|
||||
const RELEASES_URL = `https://api.github.com/repos/${OWNER}/${REPO}/releases?per_page=6`;
|
||||
const TAGS_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=6`;
|
||||
const REPO_URL = `https://github.com/${OWNER}/${REPO}`;
|
||||
|
||||
const currentVersionNode = document.querySelector("[data-current-version]");
|
||||
const currentVersionDateNode = document.querySelector("[data-current-version-date]");
|
||||
const currentReleaseNoteNode = document.querySelector("[data-current-release-note]");
|
||||
const latestDownloadNode = document.querySelector("[data-latest-download]");
|
||||
const viewMoreNode = document.querySelector("[data-view-more]");
|
||||
const versionsListNode = document.querySelector("[data-versions-list]");
|
||||
|
||||
function formatDate(value) {
|
||||
if (!value) return "";
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return "";
|
||||
return date.toLocaleDateString("fr-FR", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
}
|
||||
|
||||
function getReleaseLabel(entry) {
|
||||
return entry?.name || entry?.tag_name || entry?.ref || "Sans nom";
|
||||
}
|
||||
|
||||
function getReleasePageUrl(entry, mode) {
|
||||
if (mode === "releases") {
|
||||
return entry?.html_url || REPO_URL;
|
||||
}
|
||||
|
||||
if (entry?.name) {
|
||||
return `${REPO_URL}/releases/tag/${encodeURIComponent(entry.name)}`;
|
||||
}
|
||||
|
||||
return REPO_URL;
|
||||
}
|
||||
|
||||
function sanitizeReleaseHtml(html) {
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.innerHTML = html || "";
|
||||
|
||||
wrapper.querySelectorAll("script, iframe, object, embed, link, style").forEach((node) => node.remove());
|
||||
wrapper.querySelectorAll("*").forEach((node) => {
|
||||
Array.from(node.attributes).forEach((attribute) => {
|
||||
const name = attribute.name.toLowerCase();
|
||||
const value = attribute.value || "";
|
||||
|
||||
if (name.startsWith("on")) {
|
||||
node.removeAttribute(attribute.name);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((name === "href" || name === "src") && /^javascript:/i.test(value)) {
|
||||
node.removeAttribute(attribute.name);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return wrapper.innerHTML;
|
||||
}
|
||||
|
||||
function pickDirectDownloadUrl(release) {
|
||||
const assets = Array.isArray(release?.assets) ? release.assets : [];
|
||||
const isoAsset = assets.find((asset) => /\.iso$/i.test(asset?.name || ""));
|
||||
const directAsset = isoAsset || assets[0];
|
||||
|
||||
if (directAsset) {
|
||||
return directAsset.browser_download_url || directAsset.url || release.html_url || REPO_URL;
|
||||
}
|
||||
|
||||
return release?.zipball_url || release?.html_url || REPO_URL;
|
||||
}
|
||||
|
||||
function renderEmptyState(title, message) {
|
||||
if (!versionsListNode) return;
|
||||
versionsListNode.innerHTML = `
|
||||
<li class="version-item version-item-empty">
|
||||
<div class="version-main">
|
||||
<strong>${title}</strong>
|
||||
<span>${message}</span>
|
||||
</div>
|
||||
<div class="version-side">
|
||||
<a class="button button-secondary" href="${REPO_URL}" target="_blank" rel="noreferrer">Voir le repo</a>
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderList(items, mode) {
|
||||
if (!versionsListNode) return;
|
||||
versionsListNode.innerHTML = "";
|
||||
|
||||
const visibleItems = items.slice(1, 6);
|
||||
|
||||
if (!visibleItems.length) {
|
||||
if (items.length === 0) {
|
||||
renderEmptyState("Aucune release trouvée", "Le dépôt n'expose pas encore de release ou les releases ne sont pas accessibles.");
|
||||
} else {
|
||||
renderEmptyState(
|
||||
mode === "releases" ? "Aucune release précédente trouvée" : "Aucun tag précédent trouvé",
|
||||
mode === "releases"
|
||||
? "Il n'y a qu'une seule release publiée pour le moment."
|
||||
: "Il n'y a qu'un seul tag disponible pour le moment."
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
visibleItems.forEach((entry) => {
|
||||
const li = document.createElement("li");
|
||||
li.className = "version-item";
|
||||
|
||||
const label = getReleaseLabel(entry);
|
||||
const downloadUrl = getReleasePageUrl(entry, mode);
|
||||
const published = formatDate(entry.published_at || entry.commit?.committer?.date);
|
||||
|
||||
li.innerHTML = `
|
||||
<div class="version-main">
|
||||
<strong>${label}</strong>
|
||||
<span>${published ? `Publié le ${published}` : mode === "releases" ? "Date inconnue" : "Tag GitHub"}</span>
|
||||
</div>
|
||||
<div class="version-side">
|
||||
<a class="button button-secondary" href="${downloadUrl}" target="_blank" rel="noreferrer">Télécharger</a>
|
||||
</div>
|
||||
`;
|
||||
|
||||
versionsListNode.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchJson(url) {
|
||||
const response = await fetch(url, {
|
||||
headers: { Accept: "application/vnd.github+json" },
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`GitHub API ${response.status}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async function loadReleases() {
|
||||
if (currentVersionNode) currentVersionNode.textContent = "Chargement...";
|
||||
if (currentVersionDateNode) currentVersionDateNode.textContent = "";
|
||||
if (currentReleaseNoteNode) currentReleaseNoteNode.textContent = "";
|
||||
if (latestDownloadNode) latestDownloadNode.href = REPO_URL;
|
||||
if (viewMoreNode) viewMoreNode.href = `${REPO_URL}/releases`;
|
||||
|
||||
try {
|
||||
const releases = await fetchJson(RELEASES_URL);
|
||||
|
||||
if (Array.isArray(releases) && releases.length > 0) {
|
||||
const latest = releases[0];
|
||||
const latestLabel = getReleaseLabel(latest);
|
||||
const latestDownloadUrl = pickDirectDownloadUrl(latest);
|
||||
const latestDate = formatDate(latest.published_at);
|
||||
|
||||
if (currentVersionNode) currentVersionNode.textContent = latestLabel;
|
||||
if (currentVersionDateNode) currentVersionDateNode.textContent = latestDate ? ` · ${latestDate}` : "";
|
||||
if (currentReleaseNoteNode) {
|
||||
const bodyHtml = sanitizeReleaseHtml(latest.body || "");
|
||||
currentReleaseNoteNode.innerHTML = bodyHtml || "Release GitHub la plus récente.";
|
||||
}
|
||||
if (latestDownloadNode) latestDownloadNode.href = latest.html_url || REPO_URL;
|
||||
|
||||
renderList(releases, "releases");
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
// On tente le fallback tags ci-dessous.
|
||||
}
|
||||
|
||||
try {
|
||||
const tags = await fetchJson(TAGS_URL);
|
||||
|
||||
if (Array.isArray(tags) && tags.length > 0) {
|
||||
const latestTag = tags[0];
|
||||
const latestLabel = getReleaseLabel(latestTag);
|
||||
const latestDownloadUrl = `${REPO_URL}/releases/tag/${encodeURIComponent(latestTag.name)}`;
|
||||
|
||||
if (currentVersionNode) currentVersionNode.textContent = `${latestLabel} (tag)`;
|
||||
if (currentVersionDateNode) currentVersionDateNode.textContent = "";
|
||||
if (currentReleaseNoteNode) {
|
||||
currentReleaseNoteNode.textContent = "Aucune release GitHub publiée pour le moment, affichage des tags du dépôt.";
|
||||
}
|
||||
if (latestDownloadNode) latestDownloadNode.href = latestDownloadUrl;
|
||||
|
||||
renderList(tags, "tags");
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentVersionNode) currentVersionNode.textContent = "Aucune release trouvée";
|
||||
if (currentReleaseNoteNode) {
|
||||
currentReleaseNoteNode.textContent = "Le dépôt ne renvoie ni release ni tag public.";
|
||||
}
|
||||
renderEmptyState("Aucune release trouvée", "Le dépôt ne renvoie ni release ni tag public.");
|
||||
} catch (error) {
|
||||
if (currentVersionNode) currentVersionNode.textContent = "Impossible de charger les releases";
|
||||
if (currentReleaseNoteNode) {
|
||||
currentReleaseNoteNode.textContent = `Erreur: ${error.message}. Vérifie que le repo GitHub est public et qu'il expose des releases ou des tags.`;
|
||||
}
|
||||
renderEmptyState("Impossible de charger les releases", "Ouvre le repo GitHub pour vérifier les releases ou les tags.");
|
||||
}
|
||||
}
|
||||
|
||||
loadReleases();
|
||||
})();
|
||||
@@ -0,0 +1,114 @@
|
||||
.versions-panel {
|
||||
margin-top: 1rem;
|
||||
padding: 1rem;
|
||||
border-radius: 1rem;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid rgba(148, 163, 184, 0.08);
|
||||
}
|
||||
|
||||
.latest-version h2 {
|
||||
margin: 0 0 0.35rem;
|
||||
}
|
||||
|
||||
.latest-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.latest-meta {
|
||||
color: var(--muted);
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.latest-actions {
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.latest-note {
|
||||
margin: 0.85rem 0 0;
|
||||
color: var(--muted);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.latest-note img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
margin: 0.75rem 0;
|
||||
border-radius: 0.65rem;
|
||||
}
|
||||
|
||||
.latest-note a {
|
||||
color: #7dd3fc;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.latest-note a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.versions-archive {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.versions-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.version-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0.8rem 0.9rem;
|
||||
border-radius: 0.8rem;
|
||||
background: rgba(2, 6, 23, 0.24);
|
||||
border: 1px solid rgba(148, 163, 184, 0.08);
|
||||
}
|
||||
|
||||
.version-item-empty {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.version-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
}
|
||||
|
||||
.version-main a {
|
||||
color: #7dd3fc;
|
||||
text-decoration: none;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.version-main a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.version-side {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@media (max-width: 880px) {
|
||||
|
||||
.latest-row,
|
||||
.version-item {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.version-side {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
+66
-21
@@ -158,27 +158,6 @@ main[data-page-content] {
|
||||
}
|
||||
|
||||
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.8rem 1.2rem;
|
||||
border-radius: 999px;
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.button-primary {
|
||||
background: linear-gradient(145deg, var(--accent), var(--accent-strong));
|
||||
color: #08101d;
|
||||
}
|
||||
|
||||
.button-secondary {
|
||||
background: rgba(255,255,255,0.03);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
|
||||
.features {
|
||||
margin-top: 1.5rem;
|
||||
@@ -259,6 +238,72 @@ main[data-page-content] {
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
/* Styles page Téléchargements */
|
||||
.downloads {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.downloads-header h1 { margin: 0 0 0.35rem; }
|
||||
|
||||
.download-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.download-card {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: stretch;
|
||||
padding: 0.8rem;
|
||||
border-radius: 0.75rem;
|
||||
background: rgba(255,255,255,0.02);
|
||||
border: 1px solid rgba(148,163,184,0.06);
|
||||
}
|
||||
|
||||
.download-media { width: 220px; flex: 0 0 220px; overflow: hidden; border-radius: 0.5rem; }
|
||||
.download-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
|
||||
.download-body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 0.6rem; }
|
||||
|
||||
.download-actions { display: flex; gap: 0.6rem; margin-top: auto; }
|
||||
|
||||
@media (max-width: 880px) {
|
||||
.download-grid { grid-template-columns: 1fr; }
|
||||
.download-media { width: 140px; flex-basis: 140px; }
|
||||
}
|
||||
|
||||
/* Versions toolbar */
|
||||
.version-toolbar {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 0.8rem;
|
||||
}
|
||||
|
||||
.version-current { color: var(--muted); }
|
||||
|
||||
.versions-archive {
|
||||
margin-top: 0.8rem;
|
||||
padding: 0.8rem;
|
||||
border-radius: 0.6rem;
|
||||
background: rgba(255,255,255,0.01);
|
||||
border: 1px solid rgba(148,163,184,0.06);
|
||||
}
|
||||
|
||||
.versions-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
|
||||
.version-item { display: flex; justify-content: space-between; align-items: center; padding: 0.4rem 0.6rem; border-radius: 0.45rem; }
|
||||
.version-meta { color: var(--muted); margin-left: 0.6rem; font-size: 0.92rem; }
|
||||
.version-actions { display: flex; gap: 0.4rem; }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.version-toolbar { flex-direction: column; align-items: flex-start; }
|
||||
.version-actions { width: 100%; display: flex; gap: 0.5rem; }
|
||||
}
|
||||
|
||||
|
||||
.features .card {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
|
||||
@@ -274,6 +274,69 @@ body::after {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.8rem 1.2rem;
|
||||
border-radius: 999px;
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
transform 180ms ease,
|
||||
background-color 180ms ease,
|
||||
border-color 180ms ease,
|
||||
color 180ms ease,
|
||||
box-shadow 180ms ease,
|
||||
opacity 180ms ease;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.button-primary {
|
||||
background: linear-gradient(145deg, var(--accent), var(--accent-strong));
|
||||
color: #08101d;
|
||||
box-shadow: 0 12px 28px rgba(2, 6, 23, 0.25);
|
||||
}
|
||||
|
||||
.button-secondary {
|
||||
background: rgba(255,255,255,0.04);
|
||||
color: var(--text);
|
||||
border-color: rgba(148, 163, 184, 0.18);
|
||||
}
|
||||
|
||||
.button:hover,
|
||||
.button:focus-visible {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 14px 30px rgba(2, 6, 23, 0.28);
|
||||
}
|
||||
|
||||
.button-primary:hover,
|
||||
.button-primary:focus-visible {
|
||||
background: linear-gradient(145deg, color-mix(in srgb, var(--accent) 86%, white), color-mix(in srgb, var(--accent-strong) 92%, white));
|
||||
}
|
||||
|
||||
.button-secondary:hover,
|
||||
.button-secondary:focus-visible {
|
||||
background: rgba(125, 211, 252, 0.12);
|
||||
border-color: rgba(125, 211, 252, 0.35);
|
||||
}
|
||||
|
||||
.button:focus-visible {
|
||||
outline: 2px solid rgba(125, 211, 252, 0.85);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.button[aria-disabled="true"],
|
||||
.button:disabled {
|
||||
opacity: 0.55;
|
||||
pointer-events: none;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
+30
-14
@@ -3,24 +3,40 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>DL</title>
|
||||
<title>Téléchargements — FWS Linux</title>
|
||||
|
||||
<link rel="stylesheet" href="../assets/styles/downloads.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main data-page-content>
|
||||
<section class="hero">
|
||||
<p class="eyebrow">Route DL</p>
|
||||
<h1>Le contenu de la section DL charge ici.</h1>
|
||||
<p class="lead">
|
||||
Cette page vit dans <span>p/dl/index.html</span> et son contenu est injecté dans le
|
||||
<span>main</span> quand l'URL pointe vers <span>/?p/dl/</span>.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a class="button button-primary" href="/" data-route="home">Retour à l'accueil</a>
|
||||
<a class="button button-secondary" href="/?p/dl/" data-route="dl">Recharger DL</a>
|
||||
</div>
|
||||
<section class="downloads">
|
||||
<header class="downloads-header">
|
||||
<h1>Téléchargements</h1>
|
||||
<div class="versions-panel">
|
||||
<div class="latest-version">
|
||||
<h2>Dernière version</h2>
|
||||
<div class="latest-row">
|
||||
<div class="latest-meta">
|
||||
<strong data-current-version>Chargement...</strong>
|
||||
<span data-current-version-date></span>
|
||||
</div>
|
||||
<div class="latest-actions">
|
||||
<a class="button" data-latest-download href="#" target="_blank" rel="noreferrer">Télécharger</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="latest-note" data-current-release-note></p>
|
||||
</div>
|
||||
|
||||
<div class="versions-archive">
|
||||
<h3>5 versions précédentes</h3>
|
||||
<ul class="versions-list" data-versions-list></ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<script src="../assets/js/downloads.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user