﻿{"id":1904,"date":"2026-01-07T12:00:43","date_gmt":"2026-01-07T11:00:43","guid":{"rendered":"https:\/\/sa.fpma.church\/?p=1904"},"modified":"2026-01-18T00:25:39","modified_gmt":"2026-01-17T23:25:39","slug":"programme-2026","status":"publish","type":"post","link":"https:\/\/sa.fpma.church\/?p=1904","title":{"rendered":"\ud83d\udcd6 Timeline des dimanches 2026"},"content":{"rendered":"\n<title>Timeline Biblique 2026<\/title>\n    <style>\n\n        .mycontainer {\n            max-width: 100%;\n            margin: 0 auto;\n            background: linear-gradient(90deg, #667eea 0%, #FCBDE9 100%);\n        }\n\n        h1 {\n            text-align: center;\n            color: white;\n            margin-bottom: 30px;\n            font-size: 2.5em;\n            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);\n        }\n\n        .controls {\n            background: white;\n            padding: 25px;\n            border-radius: 12px;\n            margin-bottom: 40px;\n            box-shadow: 0 5px 20px rgba(0,0,0,0.2);\n            display: none; \t\n        }\n\n        .api-config {\n            display: grid;\n            grid-template-columns: 1fr;\n            gap: 20px;\n            margin-bottom: 20px;\n        }\n\n        .api-config input {\n            padding: 10px;\n            border: 2px solid #667eea;\n            border-radius: 6px;\n            width: 100%;\n            font-size: 0.95em;\n        }\n\n        .api-config label {\n            display: block;\n            font-weight: bold;\n            margin-bottom: 8px;\n            color: #667eea;\n        }\n\n        button {\n            background: #667eea;\n            color: white;\n            padding: 12px 30px;\n            border: none;\n            border-radius: 6px;\n            cursor: pointer;\n            font-size: 1em;\n            font-weight: bold;\n            transition: all 0.3s;\n            margin-right: 10px;\n        }\n\n        button:hover {\n            background: #764ba2;\n            transform: translateY(-2px);\n        }\n\n        button:disabled {\n            background: #ccc;\n            cursor: not-allowed;\n            transform: none;\n        }\n\n        .btn-secondary {\n            background: #4caf50;\n        }\n\n        .btn-secondary:hover {\n            background: #45a049;\n        }\n\n        .timeline {\n            position: relative;\n            padding: 20px 0;\n        }\n\n        .timeline::before {\n            content: '';\n            position: absolute;\n            left: 50%;\n            transform: translateX(-50%);\n            width: 10px;\n            height: 100%;\n            background: linear-gradient(to bottom, #701907, #F5694C);\n            border-radius: 2px;\n        }\n\n        .timeline-item {\n            position: relative;\n            margin-bottom: 50px;\n            opacity: 0;\n            transform: translateY(30px);\n            animation: fadeInUp 0.6s ease forwards;\n        }\n\n        @keyframes fadeInUp {\n            to {\n                opacity: 1;\n                transform: translateY(0);\n            }\n        }\n\n        .timeline-item:nth-child(odd) .timeline-content {\n            margin-left: auto;\n            margin-right: 50%;\n            padding-right: 40px;\n        }\n\n        .timeline-item:nth-child(even) .timeline-content {\n            margin-left: 50%;\n            padding-left: 40px;\n        }\n\n        .timeline-content {\n            background: white;\n            padding: 25px;\n            border-radius: 12px;\n            box-shadow: 0 5px 20px rgba(0,0,0,0.2);\n            transition: all 0.3s ease;\n            max-width: 650px;\n        }\n\n        .timeline-header {\n            border-bottom: 2px solid #f0f0f0;\n            padding-bottom: 15px;\n            margin-bottom: 20px;\n        }\n\n        .timeline-date {\n            font-size: 1.4em;\n            font-weight: bold;\n            color: #667eea;\n            margin-bottom: 5px;\n        }\n\n        .sunday-type {\n            display: inline-block;\n            background: #764ba2;\n            color: white;\n            padding: 4px 12px;\n            border-radius: 15px;\n            font-size: 0.85em;\n            margin-right: 10px;\n        }\n\n        .timeline-week {\n            font-size: 0.9em;\n            color: #666;\n            display: inline-block;\n        }\n\n        .theme-badge {\n            background: #fff3cd;\n            color: #856404;\n            padding: 8px 15px;\n            border-radius: 6px;\n            font-weight: bold;\n            margin-top: 10px;\n            display: inline-block;\n            border-left: 4px solid #ffc107;\n        }\n\n        .verses {\n            margin-top: 20px;\n        }\n\n        .verse-item {\n            background: #f8f9ff;\n            padding: 15px;\n            margin-bottom: 15px;\n            border-left: 4px solid #667eea;\n            border-radius: 6px;\n            transition: all 0.3s;\n            cursor: pointer;\n        }\n\n        .verse-item:hover {\n            background: #f0f2ff;\n            border-left-width: 6px;\n            transform: translateX(5px);\n        }\n\n        .verse-header {\n            display: flex;\n            flex-direction: column;  \n            justify-content: space-between;\n            align-items: flex-start;\n            margin-bottom: 5px;\n            text-align: left;\n        }\n\n        .verse-reference {\n            font-weight: bold;\n            color: #764ba2;\n            font-size: 1.05em;\n            display: flex;\n            align-items: center;\n            gap: 8px;\n        }\n\n        .verse-toggle {\n            font-size: 0.8em;\n            color: #7F6F85;\n            margin-left: left;\n        }\n\n\t\t.verse-title {\n\t\t\tcolor: #667eea;\n\t\t\tfont-size: 0.9em;\n\t\t\tfont-style: italic;\n\t\t\ttext-align: left;\n\t\t\tmax-width: 100%;\n\t\t\tmargin-top: 5px;\n\t\t}\n\n        .verse-text {\n            color: #333;\n            line-height: 1.7;\n            font-style: italic;\n            margin-top: 12px;\n            padding-left: 10px;\n            border-left: 2px solid #ddd;\n            display: none;\n            animation: slideDown 0.3s ease;\n        }\n\n        .verse-text.visible {\n            display: block;\n        }\n\n        @keyframes slideDown {\n            from {\n                opacity: 0;\n                transform: translateY(-10px);\n            }\n            to {\n                opacity: 1;\n                transform: translateY(0);\n            }\n        }\n\n        .lesson {\n            background: linear-gradient(135deg, #fff9e6 0%, #fffef0 100%);\n            padding: 20px;\n            margin-top: 20px;\n            border-radius: 8px;\n            border: 2px solid #ffd700;\n            box-shadow: 0 2px 10px rgba(255, 215, 0, 0.2);\n            cursor: pointer;\n            transition: all 0.3s;\n        }\n\n        .lesson:hover {\n            transform: translateY(-2px);\n            box-shadow: 0 4px 15px rgba(255, 215, 0, 0.3);\n        }\n\n        .lesson-title {\n            font-weight: bold;\n            color: #ff8c00;\n            font-size: 1.15em;\n            display: flex;\n            align-items: center;\n            gap: 8px;\n        }\n\n        .age-groups {\n            display: none;\n            margin-top: 15px;\n            padding-top: 15px;\n            border-top: 2px solid #ffd700;\n        }\n\n        .age-groups.visible {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));\n            gap: 10px;\n            animation: slideDown 0.3s ease;\n        }\n\n        .age-button {\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n            color: white;\n            padding: 12px 15px;\n            border: none;\n            border-radius: 8px;\n            cursor: pointer;\n            font-size: 0.95em;\n            font-weight: bold;\n            transition: all 0.3s;\n            box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);\n        }\n\n        .age-button:hover {\n            transform: translateY(-3px);\n            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.5);\n        }\n\n        .age-button:active {\n            transform: translateY(-1px);\n        }\n\n        .timeline-marker {\n            position: absolute;\n            left: 50%;\n            transform: translateX(-50%);\n            width: 20px;\n            height: 20px;\n            background: white;\n            border: 4px solid #667eea;\n            border-radius: 50%;\n            top: 20px;\n            z-index: 10;\n        }\n\n        .month-separator {\n            text-align: center;\n            margin: 60px 0 40px;\n        }\n\n        .month-separator h2 {\n            display: inline-block;\n            background: white;\n            color: #667eea;\n            padding: 10px 30px;\n            border-radius: 25px;\n            font-size: 1.5em;\n            box-shadow: 0 4px 15px rgba(0,0,0,0.2);\n        }\n\n        .status {\n            padding: 10px;\n            margin-bottom: 15px;\n            border-radius: 6px;\n            font-weight: bold;\n        }\n\n        .status.info {\n            background: #e3f2fd;\n            color: #1976d2;\n        }\n\n        .status.success {\n            background: #e8f5e9;\n            color: #388e3c;\n        }\n\n        .status.error {\n            background: #ffebee;\n            color: #c62828;\n        }\n\n        .status.warning {\n            background: #fff3e0;\n            color: #f57c00;\n        }\n\n        .progress-bar {\n            width: 100%;\n            height: 6px;\n            background: #e0e0e0;\n            border-radius: 3px;\n            overflow: hidden;\n            margin-top: 10px;\n        }\n\n        .progress-fill {\n            height: 100%;\n            background: linear-gradient(90deg, #667eea, #764ba2);\n            width: 0%;\n            transition: width 0.3s ease;\n        }\n\n        @media (max-width: 768px) {\n            .timeline::before {\n                left: 30px;\n            }\n\n            .timeline-item:nth-child(odd) .timeline-content,\n            .timeline-item:nth-child(even) .timeline-content {\n                margin-left: 60px;\n                margin-right: 0;\n                padding-left: 20px;\n                padding-right: 20px;\n                max-width: 100%;\n            }\n\n            .timeline-marker {\n                left: 30px;\n            }\n\n            .age-groups.visible {\n                grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));\n            }\n        }\n    <\/style>\n\n\n    <div class=\"mycontainer\">\n        <h1>\ud83d\udcd6 2026<\/h1>\n        \n        <div class=\"controls\" > \n            <div class=\"api-config\">\n                <div>\n                    <label>\ud83d\udd17 URL Google Sheets (publi\u00e9 en CSV) :<\/label>\n                    <input type=\"text\" id=\"sheetsUrl\" value=\"https:\/\/docs.google.com\/spreadsheets\/d\/e\/2PACX-1vTlF94nbuw-LLBPAAzsIPCFtRsL9LivUh3JEPr3FKUjIfCyuDy97-qHL4xCBAVOeg\/pub?output=csv\" placeholder=\"Fichier source googlesheet\" \/>\n                    <small style=\"color: #666; display: block; margin-top: 5px;\">\n                        \ud83d\udca1 Colonne \u00ab\u00a0idField\u00a0\u00bb obligatoire pour les PDFs\n                    <\/small>\n                <\/div>\n                <div> <!-- \u2705 AJOUT\u00c9 : Dossier Drive -->\n                    <label>\ud83d\udcc1 ID du dossier Google Drive :<\/label>\n                    <input type=\"text\" id=\"driveFolderId\" value=\"15psmJDcfC6hstG3Is26zPq7PTxUS9sP5\" placeholder=\"ID du dossier Drive\" \/>\n                <\/div>\n                <div> <!-- \u2705 AJOUT\u00c9 : Bouton Debug -->\n                    <button onclick=\"debugDates()\" style=\"background: #FF5722; padding: 8px 20px; font-size: 0.9em;\">\ud83d\udd0d Debug Dates<\/button>\n                <\/div>\n            <\/div>\n            \n            <div>\n                <button id=\"testBtn\" onclick=\"testConnection()\">\ud83e\uddea Tester<\/button>\n                <button id=\"generateBtn\" class=\"btn-secondary\" onclick=\"generateTimeline()\">\ud83d\ude80 G\u00e9n\u00e9rer<\/button>\n                <button onclick=\"autoLoad()\" style=\"background: #ff9800;\">\u26a1 Auto<\/button>\n            <\/div>         \n        <\/div>\n        <div id=\"status\"><\/div>   \n        <div class=\"timeline\" id=\"timeline\"><\/div>\n    <\/div>\n\n    <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/PapaParse\/5.3.2\/papaparse.min.js\"><\/script>\n    <script>\n        let versesData = {};\n        let sundaysData = [];\n        \n        const ageGroups = [\n            { label: '3-6 ans', filename: '3-6ans' },\n            { label: '7-8 ans', filename: '7-8ans' },\n            { label: '9-10 ans', filename: '9-10ans' },\n            { label: '11-12 ans', filename: '11-12ans' },\n            { label: '13-14 ans', filename: '13-14ans' },\n            { label: '15-16 ans', filename: '15-16ans' }\n        ];\n\n        \/\/ \u2705 TOUTES LES FONCTIONS UTILITAIRES\n        function toggleVerseText(dateKey, verseIndex) {\n            const textElement = document.getElementById(`verse-${dateKey}-${verseIndex}`);\n            textElement?.classList.toggle('visible');\n        }\n\n        function toggleAgeGroups(dateKey) {\n            const ageGroupsElement = document.getElementById(`age-groups-${dateKey}`);\n            ageGroupsElement?.classList.toggle('visible');\n        }\n\n        function formatDateForFilename(date) {\n            const day = String(date.getDate()).padStart(2, '0');\n            const month = String(date.getMonth() + 1).padStart(2, '0');\n            const year = date.getFullYear();\n            return `${day}\/${month}\/${year}`;\n        }\n\n        function formatDateKey(date) {\n            const year = date.getFullYear();\n            const month = String(date.getMonth() + 1).padStart(2, '0');\n            const day = String(date.getDate()).padStart(2, '0');\n            return `${year}-${month}-${day}`;\n        }\n\n        function formatDate(date) {\n            const months = ['Janvier','F\u00e9vrier','Mars','Avril','Mai','Juin','Juillet','Ao\u00fbt','Septembre','Octobre','Novembre','D\u00e9cembre'];\n            return `${date.getDate()} ${months[date.getMonth()]} ${date.getFullYear()}`;\n        }\n\nfunction getAllSundays2026() {\n    const sundays = [];\n    const year = 2026;\n    const today = new Date();\n    today.setHours(0, 0, 0, 0);\n    \n    for (let month = 0; month < 12; month++) {\n        const firstDay = new Date(year, month, 1);\n        const lastDay = new Date(year, month + 1, 0);\n        let day = 1;\n        while (day <= lastDay.getDate()) {\n            const currentDate = new Date(year, month, day);\n            if (currentDate.getDay() === 0) {\n               if (currentDate >= today) {\n                    sundays.push(currentDate);\n               }\n            }\n            day++;\n        }\n    }\n    return sundays;\n}\n\n        function getWeekNumber(date) {\n            const firstDayOfYear = new Date(date.getFullYear(), 0, 1);\n            const pastDaysOfYear = (date - firstDayOfYear) \/ 86400000;\n            return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) \/ 7);\n        }\n\n        function showStatus(message, type = 'info') {\n            const statusDiv = document.getElementById('status');\n            statusDiv.innerHTML = `<div class=\"status ${type}\">${message}<\/div>`;\n        }\n\n        function updateProgress(current, total) {\n            const statusDiv = document.getElementById('status');\n            const percent = Math.round((current \/ total) * 100);\n            statusDiv.innerHTML = `\n                <div class=\"status info\">\n                    \ud83d\udd04 G\u00e9n\u00e9ration en cours: ${current}\/${total} dimanches (${percent}%)\n                    <div class=\"progress-bar\">\n                        <div class=\"progress-fill\" style=\"width: ${percent}%\"><\/div>\n                    <\/div>\n                <\/div>\n            `;\n        }\n\n        \/\/ \u2705 NORMALISATION DES DATES\n        function normalizeDate(dateStr) {\n            if (!dateStr) return null;\n            dateStr = dateStr.toString().trim();\n            \n            \/\/ JJ\/MM\/AAAA \u2192 AAAA-MM-JJ\n            if (dateStr.match(\/^\\d{1,2}\\\/\\d{1,2}\\\/\\d{4}$\/)) {\n                const [day, month, year] = dateStr.split('\/');\n                return `${year}-${month.padStart(2,'0')}-${day.padStart(2,'0')}`;\n            }\n            \/\/ AAAA-MM-JJ\n            if (dateStr.match(\/^\\d{4}-\\d{1,2}-\\d{1,2}$\/)) return dateStr;\n            return dateStr;\n        }\n\n        \/\/ \u2705 FONCTION DE RECHERCHE CORRIG\u00c9E\n        async function findInGoogleSheet(searchValue, searchColumn, returnColumn = null, sheetsUrl = null) {\n            try {\n                if (!sheetsUrl) sheetsUrl = document.getElementById('sheetsUrl').value.trim();\n                if (!sheetsUrl) throw new Error('URL Google Sheets manquante');\n                \n                const response = await fetch(sheetsUrl);\n                const csvText = await response.text();\n                \n                const result = await new Promise((resolve) => {\n                    Papa.parse(csvText, {\n                        header: true, skipEmptyLines: true, dynamicTyping: false,\n                        complete: (results) => {\n                            const matchingRow = results.data.find(row => {\n                                const cellValue = row[searchColumn];\n                                if (!cellValue) return false;\n                                const normalizedSearch = normalizeDate(searchValue);\n                                const normalizedCell = normalizeDate(cellValue);\n                                return normalizedSearch === normalizedCell;\n                            });\n                            resolve(matchingRow ? (returnColumn ? matchingRow[returnColumn] || null : matchingRow) : null);\n                        }\n                    });\n                });\n                \n                if (result && returnColumn) {\n                    showStatus(`\u2705 \"${searchValue}\" \u2192 ${result}`, 'success');\n                }\n                return result;\n            } catch (error) {\n                showStatus(`\u274c Recherche: ${error.message}`, 'error');\n                throw error;\n            }\n        }\n\n        \/\/ \u2705 OPEN PDF \n\tasync function openPDFFromDrive(date, ageFilename) {\n\t    const dateStr = formatDateForFilename(date);\n            const filename = `${dateStr}_${ageFilename}.pdf`;\n            showStatus(`\ud83d\udcc4 ${filename}`, 'info');\n\t    openPDFFromDrive_reel(date, dateStr)\n\t}\n\t\t\n        async function openPDFFromDrive_reel(date, dateStr) {\n            \n            \/\/ 1. Cache\n            const dateKey = formatDateKey(date);\n            if (versesData[dateKey]?.fileId) {\n                window.open(versesData[dateKey].fieldId, '_blank');\n                showStatus(`\ud83d\udcc4 ${versesData[dateKey].fieldId}`, 'info');\n                return;\n            }\n            \n            \/\/ 2. Recherche\n            const fileId = await findInGoogleSheet(dateStr, 'Date', 'idField');\n            if (fileId) {\n                window.open(fileId, '_blank');\n                showStatus(`\ud83d\udcc4 ${fieldId}`, 'info');\n                return;\n            }\n            \n            \/\/ 3. Fallback\n            \/\/ const folderId = document.getElementById('driveFolderId').value.trim();\n            \/\/ if (folderId) {\n            \/\/    window.open(`https:\/\/drive.google.com\/drive\/folders\/${folderId}`, '_blank');\n            \/\/}\n        }\n\n        \/\/ \u2705 DEBUG DATES\n        async function debugDates() {\n            const sheetsUrl = document.getElementById('sheetsUrl').value.trim();\n            const response = await fetch(sheetsUrl);\n            const csvText = await response.text();\n            \n            Papa.parse(csvText, {\n                header: true,\n                complete: (results) => {\n                    const dates = results.data.map(row => row.Date || row.date || row.DATE).filter(Boolean).slice(0, 10);\n                    console.table(dates.map(d => ({original: d, normalized: normalizeDate(d)})));\n                    showStatus(`Debug OK: ${dates.length} dates. Voir console.`, 'success');\n                }\n            });\n        }\n\n        \/\/ \u2705 LOAD GOOGLE SHEETS CORRIG\u00c9\n        async function loadGoogleSheets(url) {\n\t\t\ttry {\n\t\t\t\tshowStatus('\ud83d\udce5 Chargement des donn\u00e9es Google Sheets...', 'info');\n                const response = await fetch(url);\n                if (!response.ok) {\n                    throw new Error('Impossible de charger le fichier. V\u00e9rifiez que le lien est public.');\n                }\n\t\t\t\t\n\t\t\t\tconst csvText = await response.text();\n\t\t\t\t\n\t\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t\tPapa.parse(csvText, {\n\t\t\t\t\t\theader: true, skipEmptyLines: true, dynamicTyping: false,\n\t\t\t\t\t\tcomplete: (results) => {\n\t\t\t\t\t\t\tif (results.errors.length > 0) {\n                                reject(new Error('Erreur de parsing CSV'));\n                                return;\n                            }\n\t\t\t\t\t\t\tversesData = {};\n\t\t\t\t\t\t\tresults.data.forEach(row => {\n\t\t\t\t\t\t\t\tconst dateValue = row['Date'] || row['date'] || row['DATE'];\n\t\t\t\t\t\t\t\tif (!dateValue) return;\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tlet dateKey;\n\t\t\t\t\t\t\t\tif (dateValue.includes('\/')) {\n\t\t\t\t\t\t\t\t\tconst [day, month, year] = dateValue.split('\/');\n\t\t\t\t\t\t\t\t\tdateKey = `${year}-${month.padStart(2,'0')}-${day.padStart(2,'0')}`;\n\t\t\t\t\t\t\t\t} else if (dateValue.includes('-')) {\n\t\t\t\t\t\t\t\t\tdateKey = dateValue.split('T')[0];\n\t\t\t\t\t\t\t\t} else return;\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\/\/ \u2705 CORRECTION : fileId au niveau de la date, pas des versets\n\t\t\t\t\t\t\t\tversesData[dateKey] = {\n\t\t\t\t\t\t\t\t\tfileId: row['idField'] || row['idFile'] || row['fileId'] || row['FileId'] || '',\n\t\t\t\t\t\t\t\t\ttypeDimanche: row['Type Dimanche'] || row['type dimanche'] || '',\n\t\t\t\t\t\t\t\t\ttheme: row['Th\u00e8me'] || row['Theme'] || row['theme'] || '',\n\t\t\t\t\t\t\t\t\tverses: [\n\t\t\t\t\t\t\t\t\t\t{\n                                            reference: row['Verset 1'] || row['Verset  1'] || row['verset 1'] || '',\n                                            title: row['Titre 1'] || row['titre 1'] || '',\n                                            text: row['Texte1'] || row['texte1'] || row['Texte 1'] || ''\n                                        },\n                                        {\n                                            reference: row['Verset 2'] || row['Verset  2'] || row['verset 2'] || '',\n                                            title: row['Titre 2'] || row['titre 2'] || '',\n                                            text: row['Texte2'] || row['texte2'] || row['Texte 2'] || ''\n                                        },\n                                        {\n                                            reference: row['Verset 3'] || row['Verset  3'] || row['verset 3'] || '',\n                                            title: row['Titre 3'] || row['titre 3'] || '',\n                                            text: row['Texte3'] || row['texte3'] || row['Texte 3'] || ''\n                                        }\n\n\t\t\t\t\t\t\t\t\t].filter(v => v.reference)\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tresolve(Object.keys(versesData).length);\n\t\t\t\t\t\t},\n\t\t\t\t\t\terror: (error) => {\n                            reject(error);\n                        }\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t  } catch (error) {\n                throw error;\n            }\n        }\n\n        \/\/ \u2705 TEST ET G\u00c9N\u00c9RATION (inchang\u00e9s mais simplifi\u00e9s)\n        async function testConnection() {\n            const url = document.getElementById('sheetsUrl').value.trim();\n            if (!url) return showStatus('\u26a0\ufe0f URL manquante', 'warning');\n            \n            document.getElementById('testBtn').disabled = true;\n            try {\n                const count = await loadGoogleSheets(url);\n                showStatus(`\u2705 ${count} dates OK`, 'success');\n            } catch (error) {\n                showStatus(`\u274c ${error.message}`, 'error');\n            } finally {\n                document.getElementById('testBtn').disabled = false;\n            }\n        }\n\n\t\/\/ \u2705 G\u00c9N\u00c9RATION TIMELINE \n        async function generateTimeline() {\n            const url = document.getElementById('sheetsUrl').value.trim();\n            if (!url) return showStatus('\u26a0\ufe0f URL manquante', 'warning');\n            \n            document.getElementById('generateBtn').disabled = true;\n            try {\n                await loadGoogleSheets(url);\n\t\t\t\t\n                if (Object.keys(versesData).length === 0) {\n                    showStatus('\u26a0\ufe0f Aucune donn\u00e9e trouv\u00e9e dans le Google Sheet', 'warning');\n                    return;\n                }\n\t\t\t\t\n                const timeline = document.getElementById('timeline');\n                timeline.innerHTML = '';\n                sundaysData = getAllSundays2026();\n                let currentMonth = -1;\n                \n                for (let i = 0; i < sundaysData.length; i++) {\n                    const sunday = sundaysData[i];\n                    const dateKey = formatDateKey(sunday);\n                    const dayData = versesData[dateKey];\n                    updateProgress(i + 1, sundaysData.length);\n                    \n                    \/\/ S\u00e9parateur mois\n                    if (sunday.getMonth() !== currentMonth) {\n                        currentMonth = sunday.getMonth();\n                        const months = ['Janvier','F\u00e9vrier','Mars','Avril','Mai','Juin','Juillet','Ao\u00fbt','Septembre','Octobre','Novembre','D\u00e9cembre'];\n\t\t\t\t\t\t\n\t\t\t\t\t\tconst monthSeparator = document.createElement('div');\n                        monthSeparator.className = 'month-separator';\n                        monthSeparator.innerHTML = `<h2>${months[currentMonth]}<\/h2>`;\n                        timeline.appendChild(monthSeparator);\n                    }\n                    \n                    const weekNum = getWeekNumber(sunday);\n                    const item = document.createElement('div');\n                    item.className = 'timeline-item';\n                    item.style.animationDelay = `${i * 0.05}s`;\n                    \n                    if (!dayData) {\n                        item.innerHTML = `\n                            <div class=\"timeline-marker\"><\/div>\n                            <div class=\"timeline-content\">\n                                <div class=\"timeline-header\">\n                                    <div class=\"timeline-date\">${formatDate(sunday)}<\/div>\n                                    <div class=\"timeline-week\">Semaine ${weekNum}<\/div>\n                                <\/div>\n                                <p style=\"color: red;\">Aucune donn\u00e9e<\/p>\n                            <\/div>`;\n\t\t\t\t\t\ttimeline.appendChild(item);\n                        continue;\n                    } \n\t\t\t\t\tconst versesHTML = dayData.verses.map((verse, j) => `\n\t\t\t\t\t\t<div class=\"verse-item\" onclick=\"toggleVerseText('${dateKey}', ${j})\">\n\t\t\t\t\t\t\t<div class=\"verse-header\">\n\t\t\t\t\t\t\t\t<div class=\"verse-reference\">\ud83d\udcd6 ${verse.reference}<\/div>\n\t\t\t\t\t\t\t\t<div class=\"verse-toggle\">\u25bc Cliquez pour afficher<\/div>\n\t\t\t\t\t\t\t\t<div class=\"verse-title\">${verse.title}<\/div>\n\t\t\t\t\t\t\t<\/div><\/div>\n\t\t\t\t\t\t\t<div class=\"verse-text\" id=\"verse-${dateKey}-${j}\">${verse.text || 'Aucun texte disponible'}<\/div>\n\t\t\t\t\t\t`).join('');\n\t\t\t\t\t\n\t\t\t\t\tconst ageButtons = ageGroups.map(g => \n\t\t\t\t\t\t`<button class=\"age-button\" onclick=\"openPDFFromDrive(new Date('${sunday.toISOString()}'), '${g.filename}')\">\ud83d\udc76 ${g.label}<\/button>`\n\t\t\t\t\t).join('');\n                        \n\t\t\t\t\titem.innerHTML = `\n\t\t\t\t\t\t<div class=\"timeline-marker\"><\/div>\n\t\t\t\t\t\t<div class=\"timeline-content\">\n\t\t\t\t\t\t\t<div class=\"timeline-header\">\n\t\t\t\t\t\t\t\t<div class=\"timeline-date\">${formatDate(sunday)}<\/div>\n\t\t\t\t\t\t\t\t<div>${dayData.typeDimanche ? `\n\t\t\t\t\t\t\t\t\t<span class=\"sunday-type\">${dayData.typeDimanche}<\/span>` : ''}\n\t\t\t\t\t\t\t\t\t<span class=\"timeline-week\">Semaine ${weekNum}<\/span>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t${dayData.theme ? `<div class=\"theme-badge\">\ud83c\udfaf ${dayData.theme}<\/div>` : ''}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t<div class=\"verses\">${versesHTML}<\/div>\n\t\t\t\t\t\t\t<div class=\"lesson\" onclick=\"toggleAgeGroups('${dateKey}')\">\n\t\t\t\t\t\t\t\t<div class=\"lesson-title\">\ud83d\udca1 Cliquez ici pour afficher les Le\u00e7ons par \u00e2ge<\/div>\n\t\t\t\t\t\t\t\t<div class=\"age-groups\" id=\"age-groups-${dateKey}\">${ageButtons}<\/div>\n\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t<\/div>`;\n                   \n                    timeline.appendChild(item);\n                }\n                showStatus('\u2705 Timeline pr\u00eate !', 'success');\n            } catch (error) {\n                showStatus(`\u274c ${error.message}`, 'error');\n            } finally {\n                document.getElementById('generateBtn').disabled = false;\n            }\n        }\n\n        async function autoLoad() {\n            await generateTimeline();\n        }\n\n        \/\/ \u2705 AUTO-LOAD\n        window.addEventListener('DOMContentLoaded', () => {\n            const url = document.getElementById('sheetsUrl').value.trim();\n            if (url) {\n                setTimeout(autoLoad, 1000);\n            }\n        });\n    <\/script>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Timeline Biblique 2026 \ud83d\udcd6 2026 \ud83d\udd17 URL Google Sheets (publi\u00e9 en CSV) : \ud83d\udca1 Colonne \u00ab\u00a0idField\u00a0\u00bb obligatoire pour les PDFs \ud83d\udcc1 ID du dossier Google Drive : \ud83d\udd0d Debug Dates \ud83e\uddea Tester \ud83d\ude80 G\u00e9n\u00e9rer \u26a1 Auto<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[54],"tags":[],"class_list":["post-1904","post","type-post","status-publish","format-standard","hentry","category-haronao-programme"],"_links":{"self":[{"href":"https:\/\/sa.fpma.church\/index.php?rest_route=\/wp\/v2\/posts\/1904","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sa.fpma.church\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sa.fpma.church\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sa.fpma.church\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sa.fpma.church\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1904"}],"version-history":[{"count":79,"href":"https:\/\/sa.fpma.church\/index.php?rest_route=\/wp\/v2\/posts\/1904\/revisions"}],"predecessor-version":[{"id":2031,"href":"https:\/\/sa.fpma.church\/index.php?rest_route=\/wp\/v2\/posts\/1904\/revisions\/2031"}],"wp:attachment":[{"href":"https:\/\/sa.fpma.church\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1904"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sa.fpma.church\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1904"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sa.fpma.church\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1904"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}