Entre l'agenda et les services, une section « Le wiki, fraîchement mis à jour » liste les six dernières pages créées ou modifiées, datées en clair — « hier », « il y a 3 jours », « le 14 septembre » — avec une pastille « Nouveau » pour les pages récentes. Les données viennent de https://wiki.alpinux.org/derniers-articles.json, écrit à chaque build du wiki (dépôt alpinux-wiki, branche accueil-derniers-articles). Un article simplement déplacé n'y figure pas : seules les modifications de contenu comptent. Si le wiki est injoignable, la section reste masquée et le reste de la page ne bouge pas. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ae3qPEPRrNkEY5LwcTTKc1
1229 lines
40 KiB
HTML
1229 lines
40 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="description" content="Alpinux — Le LUG de Savoie. Groupe d'utilisateurs de Linux et logiciels libres à Chambéry. Réunions tous les 1er et 3e jeudis du mois.">
|
||
<meta name="robots" content="index, follow">
|
||
<title>Alpinux — Le LUG de Savoie</title>
|
||
|
||
<!-- Canonical -->
|
||
<link rel="canonical" href="https://alpinux.org/">
|
||
|
||
<!-- Favicons -->
|
||
<link rel="icon" type="image/x-icon" href="https://static.alpinux.org/logo/favicon.ico">
|
||
<link rel="icon" type="image/png" sizes="32x32" href="https://static.alpinux.org/logo/favicon-32.png">
|
||
<link rel="icon" type="image/png" sizes="96x96" href="https://static.alpinux.org/logo/favicon-96.png">
|
||
<link rel="apple-touch-icon" sizes="192x192" href="https://static.alpinux.org/logo/favicon-192.png">
|
||
|
||
<!-- Open Graph -->
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:site_name" content="Alpinux">
|
||
<meta property="og:title" content="Alpinux — Le LUG de Savoie">
|
||
<meta property="og:description" content="Association dédiée à Linux et aux logiciels libres en Savoie. Réunions à la Dynamo Chambéry tous les 1er et 3e jeudis du mois.">
|
||
<meta property="og:url" content="https://alpinux.org/">
|
||
<meta property="og:image" content="https://static.alpinux.org/logo/alpinux-logo-512.png">
|
||
<meta property="og:locale" content="fr_FR">
|
||
|
||
<!-- Twitter Card -->
|
||
<meta name="twitter:card" content="summary">
|
||
<meta name="twitter:title" content="Alpinux — Le LUG de Savoie">
|
||
<meta name="twitter:description" content="Association dédiée à Linux et aux logiciels libres en Savoie.">
|
||
<meta name="twitter:image" content="https://static.alpinux.org/logo/alpinux-logo-512.png">
|
||
<meta name="twitter:site" content="@alpinux">
|
||
|
||
<!-- Structured data -->
|
||
<script type="application/ld+json">
|
||
{
|
||
"@context": "https://schema.org",
|
||
"@type": "Organization",
|
||
"name": "Alpinux",
|
||
"alternateName": "Alpinux — LUG de Savoie",
|
||
"url": "https://alpinux.org",
|
||
"logo": "https://static.alpinux.org/logo/alpinux-logo-512.png",
|
||
"description": "Association loi 1901 dédiée à Linux et aux logiciels libres en Savoie. Groupe d'utilisateurs Linux (LUG) basé à Chambéry.",
|
||
"foundingLocation": {
|
||
"@type": "Place",
|
||
"name": "Chambéry",
|
||
"address": {
|
||
"@type": "PostalAddress",
|
||
"addressLocality": "Chambéry",
|
||
"addressRegion": "Savoie",
|
||
"addressCountry": "FR"
|
||
}
|
||
},
|
||
"location": {
|
||
"@type": "Place",
|
||
"name": "Dynamo Chambéry",
|
||
"address": {
|
||
"@type": "PostalAddress",
|
||
"addressLocality": "Chambéry",
|
||
"addressRegion": "Savoie",
|
||
"addressCountry": "FR"
|
||
}
|
||
},
|
||
"sameAs": [
|
||
"https://mamot.fr/@alpinux",
|
||
"https://www.helloasso.com/associations/alpinux-le-lug-de-savoie",
|
||
"https://gitea.alpinux.org/alpinux.cedrica5l"
|
||
]
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
body {
|
||
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||
background: #f3f6fb;
|
||
color: #1a1a2e;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* ── Live banner ────────────────────────────────────────────── */
|
||
#live-banner {
|
||
background: #e8f5e9;
|
||
border-bottom: 3px solid #43a047;
|
||
color: #1b5e20;
|
||
padding: .65rem 1.5rem;
|
||
text-align: center;
|
||
font-size: .92rem;
|
||
}
|
||
.live-dot {
|
||
display: inline-block;
|
||
width: 9px; height: 9px;
|
||
background: #43a047;
|
||
border-radius: 50%;
|
||
margin-right: .45rem;
|
||
vertical-align: middle;
|
||
animation: blink 1.4s ease-in-out infinite;
|
||
}
|
||
@keyframes blink { 0%,100% { opacity: 1; } 50% { opacity: .15; } }
|
||
|
||
/* ── Header ────────────────────────────────────────────────── */
|
||
header {
|
||
background: #1a6bbf;
|
||
color: #fff;
|
||
padding: 3rem 1.5rem 2.5rem;
|
||
text-align: center;
|
||
}
|
||
|
||
.logo-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 1.2rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.logo-img {
|
||
width: 90px;
|
||
height: 90px;
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.logo-text {
|
||
text-align: left;
|
||
}
|
||
|
||
.brand {
|
||
font-size: 2.8rem;
|
||
line-height: 1;
|
||
color: #fff;
|
||
letter-spacing: -.02em;
|
||
}
|
||
|
||
.brand .light { font-weight: 300; }
|
||
.brand .bold { font-weight: 800; }
|
||
|
||
.tagline {
|
||
font-size: 1rem;
|
||
font-weight: 300;
|
||
letter-spacing: .18em;
|
||
color: rgba(255,255,255,.8);
|
||
margin-top: .3rem;
|
||
text-transform: lowercase;
|
||
}
|
||
|
||
.hero-text {
|
||
max-width: 640px;
|
||
margin: 1.8rem auto 0;
|
||
font-size: 1.05rem;
|
||
color: rgba(255,255,255,.88);
|
||
line-height: 1.7;
|
||
}
|
||
|
||
/* ── Nav links ──────────────────────────────────────────────── */
|
||
nav {
|
||
background: #0f4e8f;
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: .2rem;
|
||
flex-wrap: wrap;
|
||
padding: .4rem 1rem;
|
||
}
|
||
|
||
nav a {
|
||
color: rgba(255,255,255,.85);
|
||
text-decoration: none;
|
||
font-size: .88rem;
|
||
padding: .45rem .9rem;
|
||
border-radius: 4px;
|
||
transition: background .15s;
|
||
}
|
||
|
||
nav a:hover { background: rgba(255,255,255,.15); color: #fff; }
|
||
|
||
/* ── Main ───────────────────────────────────────────────────── */
|
||
main {
|
||
max-width: 960px;
|
||
margin: 0 auto;
|
||
padding: 3rem 1.5rem;
|
||
}
|
||
|
||
/* ── Section titles ─────────────────────────────────────────── */
|
||
.section-title {
|
||
font-size: 1.15rem;
|
||
font-weight: 700;
|
||
color: #0f4e8f;
|
||
margin-bottom: 1.2rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: .6rem;
|
||
}
|
||
|
||
.section-title::after {
|
||
content: '';
|
||
flex: 1;
|
||
height: 2px;
|
||
background: #e8f1fb;
|
||
}
|
||
|
||
.subsection-title {
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
color: #0f4e8f;
|
||
margin-bottom: .8rem;
|
||
}
|
||
|
||
/* ── Agenda two-column layout ───────────────────────────────── */
|
||
.agenda-layout {
|
||
display: grid;
|
||
grid-template-columns: auto 1fr;
|
||
gap: 2rem;
|
||
align-items: start;
|
||
margin-bottom: 3rem;
|
||
}
|
||
|
||
@media (max-width: 700px) {
|
||
.agenda-layout { grid-template-columns: 1fr; }
|
||
}
|
||
|
||
.upcoming-col { min-width: 0; }
|
||
|
||
.events-col {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: .8rem;
|
||
}
|
||
|
||
/* ── Planning grid ──────────────────────────────────────────── */
|
||
.planning-wrap {
|
||
background: #fff;
|
||
border-radius: 10px;
|
||
padding: 1.4rem;
|
||
box-shadow: 0 2px 10px rgba(26,107,191,.08);
|
||
margin-bottom: 2rem;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.planning-layout {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 6px;
|
||
}
|
||
|
||
.day-labels {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 3px;
|
||
margin-top: 22px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.day-labels span {
|
||
height: 14px;
|
||
line-height: 14px;
|
||
font-size: .68rem;
|
||
color: #bbb;
|
||
text-align: right;
|
||
min-width: 10px;
|
||
}
|
||
|
||
.planning-right {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 3px;
|
||
}
|
||
|
||
.planning-months {
|
||
display: flex;
|
||
gap: 3px;
|
||
height: 18px;
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.planning-months span {
|
||
width: 14px;
|
||
font-size: .68rem;
|
||
color: #bbb;
|
||
white-space: nowrap;
|
||
overflow: visible;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
#planning-grid {
|
||
display: grid;
|
||
grid-template-rows: repeat(7, 14px);
|
||
gap: 3px;
|
||
grid-auto-flow: column;
|
||
grid-auto-columns: 14px;
|
||
}
|
||
|
||
.day-cell {
|
||
width: 14px;
|
||
height: 14px;
|
||
border-radius: 2px;
|
||
background: #ebedf0;
|
||
}
|
||
|
||
.day-cell.past { opacity: .4; }
|
||
.day-cell.ev1 { background: #9fc5e8; cursor: pointer; }
|
||
.day-cell.ev2 { background: #1a6bbf; cursor: pointer; }
|
||
.day-cell.today { outline: 2px solid #f5a623; outline-offset: 1px; }
|
||
.day-cell[role=button]:hover { filter: brightness(1.14); }
|
||
.day-cell[role=button]:focus-visible { outline: 2px solid #f5a623; outline-offset: 1px; }
|
||
|
||
#planning-detail {
|
||
margin-top: 1rem;
|
||
padding: .75rem 1rem;
|
||
background: #f3f6fb;
|
||
border-radius: 6px;
|
||
border-left: 3px solid #1a6bbf;
|
||
font-size: .88rem;
|
||
}
|
||
|
||
#planning-detail ul { margin: .35rem 0 0 1.1rem; }
|
||
#planning-detail li { margin: .2rem 0; }
|
||
#planning-detail .ev-loc { color: #777; font-size: .83rem; }
|
||
|
||
.planning-legend {
|
||
margin-top: .9rem;
|
||
font-size: .75rem;
|
||
color: #bbb;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.lc {
|
||
display: inline-block;
|
||
width: 14px; height: 14px;
|
||
border-radius: 2px;
|
||
vertical-align: middle;
|
||
margin-right: .25rem;
|
||
}
|
||
|
||
.lc.ev1 { background: #9fc5e8; }
|
||
.lc.ev2 { background: #1a6bbf; }
|
||
.lc.today-swatch { background: #ebedf0; outline: 2px solid #f5a623; outline-offset: 1px; }
|
||
|
||
/* ── Event cards ────────────────────────────────────────────── */
|
||
.events {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||
gap: 1rem;
|
||
margin-bottom: 3rem;
|
||
}
|
||
|
||
/* upcoming list inside agenda-layout uses flex column */
|
||
#upcoming-list { display: flex; flex-direction: column; gap: .8rem; }
|
||
|
||
.event-card {
|
||
background: #fff;
|
||
border-radius: 10px;
|
||
padding: 1.2rem 1.4rem;
|
||
box-shadow: 0 2px 10px rgba(26,107,191,.1);
|
||
border-left: 4px solid #1a6bbf;
|
||
position: relative;
|
||
}
|
||
|
||
button.event-card {
|
||
text-align: left;
|
||
border: none;
|
||
width: 100%;
|
||
cursor: pointer;
|
||
font-family: inherit;
|
||
transition: transform .15s, box-shadow .15s;
|
||
}
|
||
|
||
button.event-card:hover {
|
||
transform: translateY(-3px);
|
||
box-shadow: 0 6px 20px rgba(26,107,191,.18);
|
||
}
|
||
|
||
a.event-card {
|
||
text-decoration: none;
|
||
color: inherit;
|
||
transition: transform .15s, box-shadow .15s;
|
||
display: block;
|
||
}
|
||
|
||
a.event-card:hover {
|
||
transform: translateY(-3px);
|
||
box-shadow: 0 6px 20px rgba(26,107,191,.18);
|
||
}
|
||
|
||
.event-card--more {
|
||
border-left-color: #888;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
text-align: center;
|
||
color: #555;
|
||
}
|
||
|
||
.event-card--more:hover { color: #1a6bbf; }
|
||
|
||
.event-icon { font-size: 1.8rem; margin-bottom: .4rem; }
|
||
|
||
/* ── Modal ──────────────────────────────────────────────────── */
|
||
.ev-modal {
|
||
position: fixed; inset: 0;
|
||
background: rgba(0,0,0,.45);
|
||
display: flex; align-items: center; justify-content: center;
|
||
z-index: 1000;
|
||
padding: 1rem;
|
||
}
|
||
|
||
.ev-modal[hidden] { display: none; }
|
||
|
||
.ev-modal-box {
|
||
background: #fff;
|
||
border-radius: 12px;
|
||
padding: 2rem;
|
||
max-width: 480px;
|
||
width: 100%;
|
||
position: relative;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,.25);
|
||
}
|
||
|
||
.ev-modal-close {
|
||
position: absolute; top: .8rem; right: 1rem;
|
||
background: none; border: none; font-size: 1.6rem;
|
||
cursor: pointer; color: #999; line-height: 1;
|
||
}
|
||
|
||
.ev-modal-close:hover { color: #333; }
|
||
|
||
.ev-modal-title {
|
||
font-size: 1.15rem;
|
||
font-weight: 700;
|
||
color: #0f4e8f;
|
||
margin-bottom: .8rem;
|
||
padding-right: 1.5rem;
|
||
}
|
||
|
||
.ev-modal-body { font-size: .92rem; line-height: 1.6; color: #444; }
|
||
|
||
.event-date {
|
||
font-size: .82rem;
|
||
font-weight: 700;
|
||
color: #1a6bbf;
|
||
letter-spacing: .06em;
|
||
text-transform: uppercase;
|
||
margin-bottom: .3rem;
|
||
}
|
||
|
||
.event-title { font-size: 1rem; font-weight: 600; }
|
||
.event-where { font-size: .88rem; color: #666; margin-top: .3rem; }
|
||
.ev-has-desc {
|
||
position: absolute;
|
||
top: .55rem; right: .6rem;
|
||
background: #1a6bbf;
|
||
color: #fff;
|
||
font-size: .72rem;
|
||
font-weight: 700;
|
||
font-style: normal;
|
||
line-height: 1;
|
||
padding: .2rem .4rem;
|
||
border-radius: 4px;
|
||
letter-spacing: .02em;
|
||
}
|
||
|
||
/* ── Today events ───────────────────────────────────────────── */
|
||
.event-card--today {
|
||
border-left-color: #f5a623;
|
||
background: #fffcf0;
|
||
}
|
||
|
||
.ev-today-badge {
|
||
display: inline-block;
|
||
background: #f5a623;
|
||
color: #fff;
|
||
font-size: .68rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: .06em;
|
||
padding: .15rem .5rem;
|
||
border-radius: 3px;
|
||
margin-bottom: .45rem;
|
||
}
|
||
|
||
.event-card--past {
|
||
opacity: .6;
|
||
}
|
||
|
||
/* ── Wiki : dernières mises à jour ──────────────────────────── */
|
||
#wiki-section { margin-bottom: 3rem; }
|
||
|
||
.wiki-list {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||
gap: 1rem;
|
||
}
|
||
|
||
.wiki-card {
|
||
background: #fff;
|
||
border-radius: 10px;
|
||
padding: 1.1rem 1.3rem;
|
||
box-shadow: 0 2px 10px rgba(26,107,191,.08);
|
||
border-left: 4px solid #9fc5e8;
|
||
text-decoration: none;
|
||
color: inherit;
|
||
display: block;
|
||
position: relative;
|
||
transition: transform .15s, box-shadow .15s;
|
||
}
|
||
|
||
.wiki-card:hover {
|
||
transform: translateY(-3px);
|
||
box-shadow: 0 6px 20px rgba(26,107,191,.18);
|
||
}
|
||
|
||
.wiki-card:hover .wiki-title { color: #1a6bbf; }
|
||
|
||
.wiki-when {
|
||
font-size: .78rem;
|
||
font-weight: 700;
|
||
color: #7a8ba0;
|
||
letter-spacing: .06em;
|
||
text-transform: uppercase;
|
||
margin-bottom: .3rem;
|
||
}
|
||
|
||
.wiki-title {
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
line-height: 1.35;
|
||
padding-right: 4.2rem;
|
||
}
|
||
|
||
.wiki-badge {
|
||
position: absolute;
|
||
top: .7rem; right: .7rem;
|
||
background: #f5a623;
|
||
color: #fff;
|
||
font-size: .68rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: .06em;
|
||
padding: .15rem .5rem;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.wiki-more {
|
||
margin-top: 1rem;
|
||
font-size: .88rem;
|
||
}
|
||
|
||
.wiki-more a { color: #1a6bbf; text-decoration: none; }
|
||
.wiki-more a:hover { text-decoration: underline; }
|
||
|
||
/* ── Services ───────────────────────────────────────────────── */
|
||
.services {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||
gap: 1rem;
|
||
}
|
||
|
||
.service-card {
|
||
background: #fff;
|
||
border-radius: 10px;
|
||
padding: 1.4rem;
|
||
box-shadow: 0 2px 10px rgba(26,107,191,.08);
|
||
text-align: center;
|
||
text-decoration: none;
|
||
color: #1a1a2e;
|
||
transition: transform .15s, box-shadow .15s;
|
||
display: block;
|
||
}
|
||
|
||
.service-card:hover {
|
||
transform: translateY(-3px);
|
||
box-shadow: 0 6px 20px rgba(26,107,191,.18);
|
||
color: #1a6bbf;
|
||
}
|
||
|
||
.service-icon {
|
||
font-size: 2rem;
|
||
margin-bottom: .6rem;
|
||
}
|
||
|
||
.service-name {
|
||
font-size: 1rem;
|
||
font-weight: 700;
|
||
margin-bottom: .3rem;
|
||
}
|
||
|
||
.service-desc {
|
||
font-size: .84rem;
|
||
color: #666;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
/* ── Footer ─────────────────────────────────────────────────── */
|
||
footer {
|
||
background: #0f4e8f;
|
||
color: rgba(255,255,255,.7);
|
||
text-align: center;
|
||
padding: 1.5rem;
|
||
font-size: .85rem;
|
||
}
|
||
|
||
footer a { color: rgba(255,255,255,.85); text-decoration: none; }
|
||
footer a:hover { color: #fff; }
|
||
|
||
@media (max-width: 480px) {
|
||
.brand { font-size: 2.2rem; }
|
||
.logo-img { width: 70px; height: 70px; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div id="live-banner" hidden aria-live="polite" aria-atomic="true">
|
||
<span class="live-dot" aria-hidden="true"></span>
|
||
<strong>En ce moment :</strong> <span id="live-text"></span>
|
||
</div>
|
||
|
||
<header>
|
||
<div class="logo-wrap">
|
||
<img src="https://static.alpinux.org/logo/alpinux-logo.png"
|
||
alt="Logo Alpinux — LUG de Savoie"
|
||
class="logo-img"
|
||
width="90" height="90">
|
||
<div class="logo-text">
|
||
<div class="brand">
|
||
<span class="light">A</span><span class="bold">l</span><span class="light">p</span><span class="bold">inux</span>
|
||
</div>
|
||
<div class="tagline">le LUG de Savoie</div>
|
||
</div>
|
||
</div>
|
||
<p class="hero-text">
|
||
Association loi 1901 dédiée à <strong>Linux et aux logiciels libres</strong> en Savoie.
|
||
Réunions tous les 1<sup>er</sup> et 3<sup>e</sup> jeudis du mois à la <strong>Dynamo Chambéry</strong>.
|
||
</p>
|
||
</header>
|
||
|
||
<nav aria-label="Navigation principale">
|
||
<a href="https://wiki.alpinux.org">Wiki</a>
|
||
<a href="https://portail.alpinux.org">Portail membres</a>
|
||
<a href="https://installparty.alpinux.org">Install Party</a>
|
||
<a href="https://gitea.alpinux.org">Gitea</a>
|
||
<a href="https://www.helloasso.com/associations/alpinux-le-lug-de-savoie">Adhérer</a>
|
||
</nav>
|
||
|
||
<main>
|
||
|
||
<h2 class="section-title">Agenda</h2>
|
||
|
||
<div class="agenda-layout">
|
||
|
||
<div class="planning-wrap">
|
||
<div class="planning-layout">
|
||
<div class="day-labels" aria-hidden="true">
|
||
<span>L</span><span>M</span><span>M</span><span>J</span><span>V</span><span>S</span><span>D</span>
|
||
</div>
|
||
<div class="planning-right">
|
||
<div id="planning-months" class="planning-months" aria-hidden="true"></div>
|
||
<div id="planning-grid" class="planning-grid"
|
||
role="grid" aria-label="Planning des 16 prochaines semaines"></div>
|
||
</div>
|
||
</div>
|
||
<div id="planning-detail" hidden></div>
|
||
<p class="planning-legend" aria-hidden="true">
|
||
<span><span class="lc ev1"></span>1 événement</span>
|
||
<span><span class="lc ev2"></span>2+ événements</span>
|
||
<span><span class="lc today-swatch"></span>Aujourd'hui</span>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="upcoming-col">
|
||
<div id="today-section" hidden>
|
||
<h3 class="subsection-title">Rendez-vous du jour</h3>
|
||
<div class="events-col" id="today-list" style="margin-bottom:1.4rem"></div>
|
||
</div>
|
||
<h3 class="subsection-title">Prochains rendez-vous</h3>
|
||
<div class="events-col" id="upcoming-list"></div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div id="ev-modal" class="ev-modal" hidden aria-modal="true" role="dialog" aria-labelledby="ev-modal-title">
|
||
<div class="ev-modal-box">
|
||
<button class="ev-modal-close" aria-label="Fermer">×</button>
|
||
<div id="ev-modal-title" class="ev-modal-title"></div>
|
||
<div id="ev-modal-body" class="ev-modal-body"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<section id="wiki-section" hidden>
|
||
<h2 class="section-title">Le wiki, fraîchement mis à jour</h2>
|
||
<div class="wiki-list" id="wiki-list"></div>
|
||
<p class="wiki-more">
|
||
<a href="https://wiki.alpinux.org">Parcourir tout le wiki →</a>
|
||
</p>
|
||
</section>
|
||
|
||
<h2 class="section-title">Nos services</h2>
|
||
<div class="services">
|
||
<a href="https://wiki.alpinux.org" class="service-card">
|
||
<div class="service-icon">📖</div>
|
||
<div class="service-name">Wiki</div>
|
||
<div class="service-desc">Guides, tutoriels et présentations</div>
|
||
</a>
|
||
<a href="https://portail.alpinux.org" class="service-card">
|
||
<div class="service-icon">🔐</div>
|
||
<div class="service-name">Portail membres</div>
|
||
<div class="service-desc">Espace adhérent, Nextcloud, Dolibarr</div>
|
||
</a>
|
||
<a href="https://installparty.alpinux.org" class="service-card">
|
||
<div class="service-icon">🐧</div>
|
||
<div class="service-name">Install Party</div>
|
||
<div class="service-desc">Événements & inscriptions</div>
|
||
</a>
|
||
<a href="https://gitea.alpinux.org" class="service-card">
|
||
<div class="service-icon">💻</div>
|
||
<div class="service-name">Gitea</div>
|
||
<div class="service-desc">Dépôts de code de l'association</div>
|
||
</a>
|
||
<a href="https://dynamic.alpinux.org" class="service-card">
|
||
<div class="service-icon">🎮</div>
|
||
<div class="service-name">Jeux & quiz</div>
|
||
<div class="service-desc">Mini-jeux et questionnaires interactifs</div>
|
||
</a>
|
||
</div>
|
||
|
||
</main>
|
||
|
||
<footer>
|
||
<p>© Alpinux — Association loi 1901 · Chambéry, Savoie</p>
|
||
<p style="margin-top:.4rem;">
|
||
<a href="https://wiki.alpinux.org/alpinux/">À propos</a> ·
|
||
<a href="https://wiki.alpinux.org/alpinux/faq/">FAQ</a> ·
|
||
<a href="https://www.helloasso.com/associations/alpinux-le-lug-de-savoie">Adhérer</a>
|
||
</p>
|
||
</footer>
|
||
|
||
<script>
|
||
(function () {
|
||
'use strict';
|
||
|
||
var CAL_URL = '/public-calendars/n5BWPYsxw7FCYozM';
|
||
var DISPLAY_WEEKS = 16;
|
||
var RRULE_DAY = { SU:0, MO:1, TU:2, WE:3, TH:4, FR:5, SA:6 };
|
||
|
||
/* ── ICS parsing ─────────────────────────────────────────────── */
|
||
|
||
function unfold(ics) {
|
||
return ics.replace(/\r\n[ \t]/g, '').replace(/\n[ \t]/g, '');
|
||
}
|
||
|
||
function parseICSDate(line) {
|
||
var ci = line.indexOf(':');
|
||
var left = line.slice(0, ci).toUpperCase();
|
||
var val = line.slice(ci + 1).trim();
|
||
var y = +val.slice(0,4), mo = +val.slice(4,6)-1, d = +val.slice(6,8);
|
||
if (left.indexOf('VALUE=DATE') >= 0 || /^\d{8}$/.test(val))
|
||
return { date: new Date(y, mo, d), allDay: true };
|
||
var h = +val.slice(9,11), mi = +val.slice(11,13), s = +val.slice(13,15);
|
||
var dt = val.slice(-1) === 'Z'
|
||
? new Date(Date.UTC(y, mo, d, h, mi, s))
|
||
: new Date(y, mo, d, h, mi, s);
|
||
return { date: dt, allDay: false };
|
||
}
|
||
|
||
function parseICS(text) {
|
||
var events = [], cur = null;
|
||
unfold(text).split(/\r?\n/).forEach(function(line) {
|
||
if (line === 'BEGIN:VEVENT') { cur = { exdates: [] }; return; }
|
||
if (line === 'END:VEVENT') { if (cur && cur.start) events.push(cur); cur = null; return; }
|
||
if (!cur || line.indexOf(':') < 0) return;
|
||
var ci = line.indexOf(':');
|
||
var key = line.slice(0, ci).split(';')[0].toUpperCase();
|
||
var val = line.slice(ci + 1);
|
||
if (key === 'SUMMARY') cur.title = val.replace(/\\,/g, ',').replace(/\\n/g, ' ').trim();
|
||
if (key === 'LOCATION') cur.location = val.replace(/\\,/g, ',').trim();
|
||
if (key === 'DESCRIPTION') cur.desc = val.replace(/\\,/g, ',').replace(/\\n/g, '\n').replace(/\\;/g, ';').trim();
|
||
if (key === 'DTSTART') { try { var ps = parseICSDate(line); cur.start = ps.date; cur.allDay = ps.allDay; } catch(e) {} }
|
||
if (key === 'DTEND') { try { cur.end = parseICSDate(line).date; } catch(e) {} }
|
||
if (key === 'RRULE') {
|
||
var r = {};
|
||
val.split(';').forEach(function(p) {
|
||
var eq = p.indexOf('=');
|
||
if (eq > 0) r[p.slice(0, eq)] = p.slice(eq + 1);
|
||
});
|
||
cur.rrule = r;
|
||
}
|
||
if (key === 'EXDATE') {
|
||
val.split(',').forEach(function(v) {
|
||
try { cur.exdates.push(parseICSDate(line.slice(0, ci+1) + v.trim()).date); } catch(e) {}
|
||
});
|
||
}
|
||
});
|
||
return events;
|
||
}
|
||
|
||
/* ── RRULE expansion ─────────────────────────────────────────── */
|
||
|
||
function localDateStr(d) {
|
||
return d.getFullYear() + '-' +
|
||
String(d.getMonth()+1).padStart(2,'0') + '-' +
|
||
String(d.getDate()).padStart(2,'0');
|
||
}
|
||
|
||
function parseRRuleUntil(s) {
|
||
var v = s.replace('Z','');
|
||
return new Date(+v.slice(0,4), +v.slice(4,6)-1, +v.slice(6,8));
|
||
}
|
||
|
||
function getNthWeekday(year, month, jsDay, n) {
|
||
var d;
|
||
if (n > 0) {
|
||
d = new Date(year, month, 1);
|
||
while (d.getDay() !== jsDay) d.setDate(d.getDate() + 1);
|
||
d.setDate(d.getDate() + (n-1) * 7);
|
||
return d.getMonth() === month ? d : null;
|
||
}
|
||
d = new Date(year, month+1, 0);
|
||
while (d.getDay() !== jsDay) d.setDate(d.getDate() - 1);
|
||
d.setDate(d.getDate() + (n+1) * 7);
|
||
return d.getMonth() === month ? d : null;
|
||
}
|
||
|
||
function expandEvent(ev, from, to) {
|
||
if (!ev.rrule) {
|
||
return (ev.start >= from && ev.start <= to)
|
||
? [{ title: ev.title, location: ev.location, desc: ev.desc, allDay: ev.allDay, start: ev.start, end: ev.end }]
|
||
: [];
|
||
}
|
||
var rule = ev.rrule;
|
||
var freq = rule.FREQ;
|
||
var ivl = +(rule.INTERVAL || 1);
|
||
var cnt = +(rule.COUNT || 9999);
|
||
var until = rule.UNTIL ? parseRRuleUntil(rule.UNTIL) : null;
|
||
var dur = ev.end ? (ev.end - ev.start) : 0;
|
||
var exSet = {};
|
||
(ev.exdates || []).forEach(function(d) { exSet[localDateStr(d)] = true; });
|
||
var results = [];
|
||
|
||
function emit(d) {
|
||
if (exSet[localDateStr(d)]) return;
|
||
if (d < from || d > to) return;
|
||
results.push({ title: ev.title, location: ev.location, desc: ev.desc, allDay: ev.allDay,
|
||
start: d, end: dur ? new Date(d.getTime() + dur) : null });
|
||
}
|
||
|
||
var cur, y, m, n, occ, pm, pos;
|
||
|
||
if (freq === 'MONTHLY') {
|
||
y = from.getFullYear(); m = from.getMonth() - ivl; n = 0;
|
||
while (n < cnt) {
|
||
m += ivl;
|
||
while (m > 11) { m -= 12; y++; }
|
||
if (new Date(y, m, 1) > to) break;
|
||
if (until && new Date(y, m, 1) > until) break;
|
||
occ = null;
|
||
if (rule.BYDAY) {
|
||
pm = /^(-?\d+)?([A-Z]{2})$/.exec(rule.BYDAY);
|
||
if (pm) {
|
||
pos = rule.BYSETPOS ? +rule.BYSETPOS : (pm[1] ? +pm[1] : 1);
|
||
occ = getNthWeekday(y, m, RRULE_DAY[pm[2]], pos);
|
||
}
|
||
} else {
|
||
occ = new Date(y, m, ev.start.getDate());
|
||
}
|
||
if (occ) {
|
||
occ.setHours(ev.start.getHours(), ev.start.getMinutes(), ev.start.getSeconds());
|
||
if (occ >= ev.start) emit(occ);
|
||
}
|
||
n++;
|
||
}
|
||
} else if (freq === 'WEEKLY') {
|
||
cur = new Date(ev.start); n = 0;
|
||
while (cur < from) cur.setDate(cur.getDate() + 7 * ivl);
|
||
cur.setDate(cur.getDate() - 7 * ivl);
|
||
while (n < cnt && cur <= to) {
|
||
if (until && cur > until) break;
|
||
emit(new Date(cur));
|
||
cur.setDate(cur.getDate() + 7 * ivl);
|
||
n++;
|
||
}
|
||
} else if (freq === 'YEARLY') {
|
||
cur = new Date(ev.start); n = 0;
|
||
while (cur.getFullYear() < from.getFullYear()) cur.setFullYear(cur.getFullYear() + ivl);
|
||
if (cur > from) cur.setFullYear(cur.getFullYear() - ivl);
|
||
while (n < cnt && cur <= to) {
|
||
if (until && cur > until) break;
|
||
emit(new Date(cur));
|
||
cur.setFullYear(cur.getFullYear() + ivl);
|
||
n++;
|
||
}
|
||
}
|
||
return results;
|
||
}
|
||
|
||
function expandAll(events, from, to) {
|
||
var out = [];
|
||
events.forEach(function(ev) {
|
||
expandEvent(ev, from, to).forEach(function(o) { out.push(o); });
|
||
});
|
||
return out;
|
||
}
|
||
|
||
/* ── Rendering ───────────────────────────────────────────────── */
|
||
|
||
function fmtDate(d, allDay) {
|
||
var s = d.toLocaleDateString('fr-FR', { weekday:'long', day:'numeric', month:'long' });
|
||
if (!allDay) s += ' à ' + d.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' });
|
||
return s;
|
||
}
|
||
|
||
function fmtTime(d) {
|
||
return d.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' });
|
||
}
|
||
|
||
function buildGrid(expanded) {
|
||
var now = new Date();
|
||
var todayKey = localDateStr(now);
|
||
|
||
var weekStart = new Date(now);
|
||
weekStart.setDate(now.getDate() - ((now.getDay() + 6) % 7));
|
||
weekStart.setHours(0, 0, 0, 0);
|
||
|
||
var evMap = {};
|
||
expanded.forEach(function(ev) {
|
||
var k = localDateStr(ev.start);
|
||
if (!evMap[k]) evMap[k] = [];
|
||
evMap[k].push(ev);
|
||
});
|
||
|
||
/* Month labels */
|
||
var monthsEl = document.getElementById('planning-months');
|
||
var lastMonth = -1;
|
||
monthsEl.innerHTML = '';
|
||
for (var w = 0; w < DISPLAY_WEEKS; w++) {
|
||
var md = new Date(weekStart);
|
||
md.setDate(weekStart.getDate() + w * 7);
|
||
var span = document.createElement('span');
|
||
if (md.getMonth() !== lastMonth) {
|
||
span.textContent = md.toLocaleDateString('fr-FR', { month: 'short' });
|
||
lastMonth = md.getMonth();
|
||
}
|
||
monthsEl.appendChild(span);
|
||
}
|
||
|
||
/* Grid cells */
|
||
var grid = document.getElementById('planning-grid');
|
||
var detail = document.getElementById('planning-detail');
|
||
grid.innerHTML = '';
|
||
|
||
for (var w = 0; w < DISPLAY_WEEKS; w++) {
|
||
for (var dd = 0; dd < 7; dd++) {
|
||
var cd = new Date(weekStart);
|
||
cd.setDate(weekStart.getDate() + w * 7 + dd);
|
||
var key = localDateStr(cd);
|
||
var dayEvs = evMap[key] || [];
|
||
var past = cd < now && key !== todayKey;
|
||
|
||
var cell = document.createElement('div');
|
||
cell.className = 'day-cell' +
|
||
(dayEvs.length === 1 ? ' ev1' : dayEvs.length >= 2 ? ' ev2' : '') +
|
||
(past ? ' past' : '') +
|
||
(key === todayKey ? ' today' : '');
|
||
|
||
if (dayEvs.length) {
|
||
cell.title = cd.toLocaleDateString('fr-FR', { weekday:'long', day:'numeric', month:'long' }) +
|
||
'\n' + dayEvs.map(function(e) { return '• ' + e.title; }).join('\n');
|
||
cell.setAttribute('role', 'button');
|
||
cell.setAttribute('tabindex', '0');
|
||
(function(dateCopy, evsCopy) {
|
||
cell.addEventListener('click', function() { showDetail(dateCopy, evsCopy, detail); });
|
||
cell.addEventListener('keydown', function(e) {
|
||
if (e.key === 'Enter' || e.key === ' ') showDetail(dateCopy, evsCopy, detail);
|
||
});
|
||
})(new Date(cd), dayEvs.slice());
|
||
}
|
||
grid.appendChild(cell);
|
||
}
|
||
}
|
||
}
|
||
|
||
function showDetail(date, evs, el) {
|
||
el.hidden = false;
|
||
el.innerHTML = '<strong>' +
|
||
date.toLocaleDateString('fr-FR', { weekday:'long', day:'numeric', month:'long', year:'numeric' }) +
|
||
'</strong><ul>' +
|
||
evs.map(function(e) {
|
||
var time = e.allDay ? '' :
|
||
' <span class="ev-loc">' +
|
||
e.start.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' }) + '</span>';
|
||
return '<li>' + e.title + time +
|
||
(e.desc ? ' <span class="ev-has-desc" title="Descriptif disponible">ℹ</span>' : '') +
|
||
(e.location ? '<span class="ev-loc"> — ' + e.location + '</span>' : '') +
|
||
'</li>';
|
||
}).join('') + '</ul>';
|
||
el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||
}
|
||
|
||
function openModal(ev) {
|
||
var modal = document.getElementById('ev-modal');
|
||
var title = document.getElementById('ev-modal-title');
|
||
var body = document.getElementById('ev-modal-body');
|
||
var opts = { weekday:'long', day:'numeric', month:'long', year:'numeric' };
|
||
var timeOpts = { hour:'2-digit', minute:'2-digit' };
|
||
var dateStr = ev.start.toLocaleDateString('fr-FR', opts);
|
||
if (!ev.allDay) {
|
||
dateStr += ' à ' + ev.start.toLocaleTimeString('fr-FR', timeOpts);
|
||
if (ev.end) dateStr += ' – ' + ev.end.toLocaleTimeString('fr-FR', timeOpts);
|
||
}
|
||
title.textContent = ev.title;
|
||
var descHtml = '';
|
||
if (ev.desc) {
|
||
descHtml = '<p style="margin-top:.8rem;color:#555;font-size:.88rem;white-space:pre-line">' +
|
||
ev.desc.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>') + '</p>';
|
||
}
|
||
body.innerHTML =
|
||
'<p>📅 ' + dateStr + '</p>' +
|
||
(ev.location ? '<p style="margin-top:.4rem">📍 ' + ev.location + '</p>' : '') +
|
||
descHtml;
|
||
modal.hidden = false;
|
||
document.getElementById('ev-modal').querySelector('.ev-modal-close').focus();
|
||
}
|
||
|
||
function buildTodayEvents(expanded) {
|
||
var now = new Date();
|
||
var todayKey = localDateStr(now);
|
||
var todayEvs = expanded
|
||
.filter(function(e) { return localDateStr(e.start) === todayKey; })
|
||
.sort(function(a, b) { return a.start - b.start; });
|
||
|
||
var section = document.getElementById('today-section');
|
||
if (!todayEvs.length) { section.hidden = true; return; }
|
||
|
||
section.hidden = false;
|
||
var container = document.getElementById('today-list');
|
||
container.innerHTML = '';
|
||
todayEvs.forEach(function(ev) {
|
||
var card = document.createElement('button');
|
||
card.className = 'event-card event-card--today';
|
||
var isPast = ev.end ? ev.end < now : (!ev.allDay && ev.start < now);
|
||
if (isPast) card.classList.add('event-card--past');
|
||
card.innerHTML =
|
||
'<span class="ev-today-badge">Aujourd\'hui</span>' +
|
||
(ev.desc ? '<span class="ev-has-desc" title="Descriptif disponible">ℹ</span>' : '') +
|
||
'<div class="event-date">' +
|
||
(ev.allDay ? 'Toute la journée'
|
||
: ev.start.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' }) +
|
||
(ev.end ? ' – ' + ev.end.toLocaleTimeString('fr-FR', { hour:'2-digit', minute:'2-digit' }) : '')) +
|
||
'</div>' +
|
||
'<div class="event-title">' + ev.title + '</div>' +
|
||
(ev.location ? '<div class="event-where">📍 ' + ev.location + '</div>' : '');
|
||
card.addEventListener('click', function() { openModal(ev); });
|
||
container.appendChild(card);
|
||
});
|
||
}
|
||
|
||
function buildUpcoming(expanded) {
|
||
var now = new Date();
|
||
var todayKey = localDateStr(now);
|
||
var upcoming = expanded
|
||
.filter(function(e) { return e.start >= now && localDateStr(e.start) !== todayKey; })
|
||
.sort(function(a, b) { return a.start - b.start; })
|
||
.slice(0, 5);
|
||
|
||
var container = document.getElementById('upcoming-list');
|
||
container.innerHTML = '';
|
||
upcoming.forEach(function(ev) {
|
||
var card = document.createElement('button');
|
||
card.className = 'event-card';
|
||
card.innerHTML =
|
||
(ev.desc ? '<span class="ev-has-desc" title="Descriptif disponible">ℹ</span>' : '') +
|
||
'<div class="event-date">' + fmtDate(ev.start, ev.allDay) + '</div>' +
|
||
'<div class="event-title">' + ev.title + '</div>' +
|
||
(ev.location ? '<div class="event-where">📍 ' + ev.location + '</div>' : '');
|
||
card.addEventListener('click', function() { openModal(ev); });
|
||
container.appendChild(card);
|
||
});
|
||
|
||
var more = document.createElement('a');
|
||
more.className = 'event-card event-card--more';
|
||
more.href = 'https://wiki.alpinux.org/alpinux/evenements/';
|
||
more.innerHTML =
|
||
'<div class="event-icon">📅</div>' +
|
||
'<div class="event-title">Tous les événements</div>' +
|
||
'<div class="event-where">wiki.alpinux.org/alpinux/evenements</div>';
|
||
container.appendChild(more);
|
||
}
|
||
|
||
/* ── Modal close handlers ────────────────────────────────────── */
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
var modal = document.getElementById('ev-modal');
|
||
modal.querySelector('.ev-modal-close').addEventListener('click', function() {
|
||
modal.hidden = true;
|
||
});
|
||
modal.addEventListener('click', function(e) {
|
||
if (e.target === modal) modal.hidden = true;
|
||
});
|
||
document.addEventListener('keydown', function(e) {
|
||
if (e.key === 'Escape') modal.hidden = true;
|
||
});
|
||
});
|
||
|
||
function updateBanner(allEvents) {
|
||
var now = new Date();
|
||
var from = new Date(now.getTime() - 86400000);
|
||
var to = new Date(now.getTime() + 86400000);
|
||
var active = expandAll(allEvents, from, to).filter(function(e) {
|
||
return e.end && e.start <= now && e.end >= now;
|
||
});
|
||
var banner = document.getElementById('live-banner');
|
||
var liveText = document.getElementById('live-text');
|
||
if (active.length) {
|
||
liveText.textContent = active.map(function(e) {
|
||
return e.title +
|
||
(e.location ? ' — ' + e.location : '') +
|
||
(e.end ? ' (jusqu’à ' + fmtTime(e.end) + ')' : '');
|
||
}).join(' | ');
|
||
banner.hidden = false;
|
||
} else {
|
||
banner.hidden = true;
|
||
}
|
||
}
|
||
|
||
/* ── Main ────────────────────────────────────────────────────── */
|
||
|
||
async function load() {
|
||
var allEvents = [];
|
||
try {
|
||
var r = await fetch(CAL_URL);
|
||
if (r.ok) {
|
||
var text = await r.text();
|
||
if (text.trim()) allEvents = parseICS(text);
|
||
}
|
||
} catch(_) {}
|
||
|
||
var now = new Date();
|
||
|
||
var weekStart = new Date(now);
|
||
weekStart.setDate(now.getDate() - ((now.getDay() + 6) % 7));
|
||
weekStart.setHours(0, 0, 0, 0);
|
||
|
||
var gridTo = new Date(weekStart);
|
||
gridTo.setDate(weekStart.getDate() + DISPLAY_WEEKS * 7);
|
||
|
||
var gridExpanded = expandAll(allEvents, weekStart, gridTo);
|
||
buildGrid(gridExpanded);
|
||
buildTodayEvents(gridExpanded);
|
||
buildUpcoming(gridExpanded);
|
||
updateBanner(allEvents);
|
||
|
||
setInterval(function() { updateBanner(allEvents); }, 60000);
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', load);
|
||
})();
|
||
</script>
|
||
|
||
<!-- ── Wiki : dernières pages créées ou modifiées ───────────────────── -->
|
||
<script>
|
||
(function () {
|
||
var URL_JSON = 'https://wiki.alpinux.org/derniers-articles.json';
|
||
var NB_MAX = 6; /* articles affichés sur l'accueil */
|
||
|
||
/* Le wiki date ses pages avec l'horodatage du commit qui a touché leur contenu :
|
||
un article déplacé ou renommé n'apparaît donc pas ici. */
|
||
|
||
function echappe(texte) {
|
||
var d = document.createElement('div');
|
||
d.textContent = texte;
|
||
return d.innerHTML;
|
||
}
|
||
|
||
function quand(iso) {
|
||
var d = new Date(iso);
|
||
if (isNaN(d)) return '';
|
||
var jour = new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
||
var auj = new Date();
|
||
auj.setHours(0, 0, 0, 0);
|
||
var jours = Math.round((auj - jour) / 86400000);
|
||
|
||
if (jours <= 0) return "aujourd'hui";
|
||
if (jours === 1) return 'hier';
|
||
if (jours < 7) return 'il y a ' + jours + ' jours';
|
||
if (jours < 14) return 'la semaine dernière';
|
||
|
||
var opts = { day: 'numeric', month: 'long' };
|
||
if (d.getFullYear() !== auj.getFullYear()) opts.year = 'numeric';
|
||
return 'le ' + d.toLocaleDateString('fr-FR', opts);
|
||
}
|
||
|
||
function carte(article) {
|
||
var a = document.createElement('a');
|
||
a.className = 'wiki-card';
|
||
a.href = article.url;
|
||
a.innerHTML =
|
||
(article.nouveau ? '<span class="wiki-badge">Nouveau</span>' : '') +
|
||
'<div class="wiki-when">' +
|
||
(article.nouveau ? 'publié ' : 'mis à jour ') + echappe(quand(article.date)) +
|
||
'</div>' +
|
||
'<div class="wiki-title">' + echappe(article.titre) + '</div>';
|
||
return a;
|
||
}
|
||
|
||
async function charger() {
|
||
var articles;
|
||
try {
|
||
var r = await fetch(URL_JSON, { cache: 'no-cache' });
|
||
if (!r.ok) return;
|
||
articles = (await r.json()).articles;
|
||
} catch (_) { return; } /* wiki injoignable : la section reste masquée */
|
||
|
||
if (!Array.isArray(articles) || !articles.length) return;
|
||
|
||
var liste = document.getElementById('wiki-list');
|
||
articles.slice(0, NB_MAX).forEach(function (article) {
|
||
if (article && article.url && article.titre) liste.appendChild(carte(article));
|
||
});
|
||
|
||
if (liste.children.length) document.getElementById('wiki-section').hidden = false;
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', charger);
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|