jQuery(document).ready(function($) { const RESULTS_CONTAINER_SELECTOR = '.sncf-board-container'; // Rechargement toutes les 90 secondes (1 min 30 s) const AUTO_REFRESH_SECONDS = 90; let allJourneys = []; // =============================================== // FONCTIONS D'EXTRACTION DE DONNÉES // =============================================== /** Extrait le mode (TER/Transilien N/Train) */ function getTrainMode(journey) { if (!journey.sections || journey.sections.length === 0) return 'Train'; const firstPublicSection = journey.sections.find(s => s.type === 'public_transport'); if (!firstPublicSection || !firstPublicSection.display_informations) return 'Train'; const di = firstPublicSection.display_informations; const lineName = di.label ? di.label.toUpperCase() : (di.code ? di.code.toUpperCase() : ''); const network = di.network ? di.network.toLowerCase() : ''; const commercialMode = di.commercial_mode ? di.commercial_mode.toLowerCase() : ''; // VÉRIFICATION 1 : TER if (network.includes('ter') || commercialMode.includes('ter')) { return 'TER'; } // VÉRIFICATION 2 : Transilien (Ligne N) if (network.includes('transilien') || lineName === 'N' || di.code === 'N') { return 'Transilien N'; } // VÉRIFICATION 3 : Autres trains if (commercialMode.includes('train')) { return 'Train'; } return 'Train'; } /** Extrait l'heure réelle, le retard et le statut. */ function getTimeAndStatus(journey) { if (!journey.departure_date_time) return { time: 'N/A', delayMin: 0, leftStatus: 'N/A' }; const dtReal = journey.departure_date_time; const firstPublicSection = journey.sections.find(s => s.type === 'public_transport'); const firstStop = firstPublicSection?.stop_date_times?.[0]; const dtBase = firstStop?.base_departure_date_time || dtReal; const formatTime = (dt) => Date.parse(dt.replace(/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/, '$1-$2-$3T$4:$5:$6')); const realTime = formatTime(dtReal); const baseTime = formatTime(dtBase); const timeStr = dtReal.substring(9, 15); const departureTime = timeStr.substring(0, 2) + ':' + timeStr.substring(2, 4); let delayMin = 0; if (realTime && baseTime) { delayMin = Math.round((realTime - baseTime) / 60000); } let leftStatus = "à l'heure"; if (delayMin > 0) { leftStatus = `retard ${delayMin} min`; } else if (delayMin < 0) { leftStatus = `avance ${Math.abs(delayMin)} min`; } return { time: departureTime, delayMin: delayMin, leftStatus: leftStatus }; } /** Extrait la voie (quai) et sa source. */ function getPlatform(journey) { if (!journey.sections || journey.sections.length === 0) return { platform: 'NC', source: 'none' }; const firstPublicSection = journey.sections.find(s => s.type === 'public_transport'); if (!firstPublicSection) return { platform: 'NC', source: 'none' }; let platform = firstPublicSection.from && firstPublicSection.from.platform ? firstPublicSection.from.platform.trim() : ''; if (!platform && firstPublicSection.stop_date_times) { const firstStop = firstPublicSection.stop_date_times[0]; if (firstStop && firstStop.platform) { platform = firstStop.platform.trim(); } } if (!platform && firstPublicSection.stop_date_times) { const firstStop = firstPublicSection.stop_date_times[0]; const pubSection = firstStop.stop_point ? firstStop.stop_point.administrative_regions : null; if (pubSection && pubSection.length > 0 && pubSection[0].platform) { platform = pubSection[0].platform.trim(); } } if (platform && platform !== '-' && platform !== ' ') { return { platform: platform, source: 'confirmed' }; } return { platform: 'NC', source: 'none' }; } /** Extrait la taille du train (Long/Court). */ function getTrainBadge(journey) { if (!journey.sections || journey.sections.length === 0) return 'Direct'; const firstPublicSection = journey.sections.find(s => s.type === 'public_transport'); if (!firstPublicSection || !firstPublicSection.display_informations) return 'Direct'; const di = firstPublicSection.display_informations; if (di.description) { const desc = di.description.toLowerCase(); if (desc.includes('court') && !desc.includes('long')) return 'Court'; if (desc.includes('long') && !desc.includes('court')) return 'Long'; } if (di.label) { const label = di.label.toLowerCase(); if (label.includes('court')) return 'Court'; if (label.includes('long')) return 'Long'; } return 'Direct'; } /** Formate la durée en heures et minutes */ function formatDuration(durationSeconds) { const h = Math.floor(durationSeconds / 3600); const m = Math.round((durationSeconds % 3600) / 60); if (h > 0) return `${h}h ${m}min`; return `${m} min`; } /** Construit l'objet de données de la ligne à afficher */ function mapJourneyToRow(journey) { const timeStatus = getTimeAndStatus(journey); const platformInfo = getPlatform(journey); if (!journey.sections || journey.sections.length === 0) { return { time: timeStatus.time, dest: 'Destination inconnue', trainNo: '', line: '', mode: 'Train', leftStatus: timeStatus.leftStatus, delayMin: timeStatus.delayMin, platform: 'NC', platformSource: 'none', badge: 'Direct', duration: '' }; } const firstPublicSection = journey.sections.find(s => s.type === 'public_transport'); const di = firstPublicSection ? firstPublicSection.display_informations : null; return { time: timeStatus.time, dest: di && di.headsign ? di.headsign : 'Destination inconnue', trainNo: di && di.headsign ? di.headsign : '', line: di && di.label ? di.label : '', mode: getTrainMode(journey), leftStatus: timeStatus.leftStatus, delayMin: timeStatus.delayMin, platform: platformInfo.platform, platformSource: platformInfo.source, badge: getTrainBadge(journey), duration: formatDuration(journey.duration) }; } // Fonction de filtrage neutre (accepte tous les trajets trouvés) function isAllowedDestination(journey) { return true; } // =============================================== // FILTRAGE ET RENDU // =============================================== /** Filtre les trajets selon le bouton sélectionné */ function applyClientFilter(filterValue) { const filtered = allJourneys.filter(row => { if (filterValue === 'all') return true; if (filterValue === 'transilien' && row.mode === 'Transilien N') return true; if (filterValue === 'ter' && row.mode === 'TER') return true; return false; }); renderBoard(filtered); } /** Génère et affiche la structure du panneau HTML */ function renderBoard(rows) { const container = $(RESULTS_CONTAINER_SELECTOR); let html = ''; if (rows.length === 0) { html += '
Aucun train trouvé correspondant aux critères.
'; } else { html += '
'; rows.forEach(r => { const leftCls = r.delayMin > 0 ? 'is-late' : (r.delayMin < 0 ? 'is-early' : 'is-ontime'); const plat = r.platform || 'NC'; const platSrc = r.platformSource || 'none'; const platCls = `sncf-platform sncf-platform--${platSrc}`; const platTitle = platSrc === 'likely' ? 'Voie indicative' : (platSrc === 'confirmed' ? 'Voie confirmée' : 'Voie non publiée'); let subParts = []; if (r.duration) { subParts.push(r.duration); } if (r.line) { subParts.push('Ligne ' + r.line); } const sub = subParts.join(' | '); const mode_class = r.mode ? r.mode.toLowerCase().replace(/\s/g, '-') : 'train'; const mode_label = r.mode === 'Transilien N' ? 'N' : r.mode; html += `
${r.leftStatus}
${r.time}
${r.mode ? `${mode_label}` : ''} ${r.dest} ${r.badge ? `${r.badge}` : ''}
${sub || ' '}
${plat}
`; }); html += '
'; } container.html(html); } // =============================================== // GESTION DU FORMULAIRE ET APPEL AJAX // =============================================== /** Génère l'heure actuelle au format Navitia (AAAAMMJJTHHMMSS) */ function formatCurrentNavitiaTime() { const d = new Date(); const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); const h = String(d.getHours()).padStart(2, '0'); const min = String(d.getMinutes()).padStart(2, '0'); const s = String(d.getSeconds()).padStart(2, '0'); return `${y}${m}${day}T${h}${min}${s}`; } function fetchAndRenderData(form) { const container = form.find(RESULTS_CONTAINER_SELECTOR); const submitBtn = form.find('button[type="submit"]'); const fromId = form.data('from'); const toId = form.data('to'); const datetimeValue = formatCurrentNavitiaTime(); const filterBtns = form.find('.sncf-filter-buttons'); const data = { action: 'sncf_fetch_horaires', security: sncf_ajax.nonce, from_id: fromId, to_id: toId, datetime: datetimeValue, count: 30 }; submitBtn.prop('disabled', true); container.html('
Recherche en cours...
'); filterBtns.find('button').prop('disabled', true); $.post(sncf_ajax.ajax_url, data) .done(function(response) { if (response.success) { allJourneys = response.data .filter(journey => isAllowedDestination(journey)) .map(mapJourneyToRow); filterBtns.find('button').prop('disabled', false); const activeFilter = filterBtns.find('button.active').data('filter') || 'ter'; applyClientFilter(activeFilter); } else { container.html('
Erreur de recherche: ' + response.data + '
'); filterBtns.find('button').prop('disabled', true); } }) .fail(function() { container.html('
Erreur de communication AJAX.
'); filterBtns.find('button').prop('disabled', true); }) .always(function() { submitBtn.prop('disabled', false); }); } // 1. Déclenchement au chargement initial $('.sncf-search-form').each(function() { fetchAndRenderData($(this)); }); // 2. Clic sur les boutons de filtrage $('.sncf-filter-buttons button').on('click', function() { const btn = $(this); const filter = btn.data('filter'); btn.addClass('active').siblings().removeClass('active'); applyClientFilter(filter); }); // 3. Auto-refresh if (!window.__hor2_refresh_global) { window.__hor2_refresh_global = true; setInterval(function(){ $('.sncf-search-form').each(function() { fetchAndRenderData($(this)); }); }, AUTO_REFRESH_SECONDS * 1000); } }); Alpha ok2

Alpha ok2

Retour en haut