{"id":6424,"date":"2026-06-08T04:46:03","date_gmt":"2026-06-08T04:46:03","guid":{"rendered":"https:\/\/eievss.com\/website\/wordpress\/?page_id=6424"},"modified":"2026-06-20T06:52:56","modified_gmt":"2026-06-20T06:52:56","slug":"endurance-race-results","status":"publish","type":"page","link":"https:\/\/eievss.com\/website\/wordpress\/endurance-race-results\/","title":{"rendered":"Endurance Race Results"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"6424\" class=\"elementor elementor-6424\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-512f216 e-flex e-con-boxed e-con e-parent\" data-id=\"512f216\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f134d28 elementor-widget elementor-widget-shortcode\" data-id=\"f134d28\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\">    <div id=\"eiev_rp_743758\" class=\"eiev-rp\"\n         data-event-id=\"\"\n         data-race-id=\"\"\n         data-race-id-fallback=\"36735\"\n         data-title=\"EIEV Results\"\n         data-live-socket=\"yes\">\n\n        <section class=\"eiev-rp-hero\">\n            <div>\n                <div class=\"eiev-rp-eyebrow\">Emirates International Endurance Village<\/div>\n                <h1>EIEV Results<\/h1>\n                <p>Live events, race cards, gates, riders and results.<\/p>\n            <\/div>\n            <div class=\"eiev-rp-hero-status\">\n                <span class=\"eiev-rp-dot\"><\/span>\n                <span class=\"eiev-rp-status\">Loading...<\/span>\n            <\/div>\n        <\/section>\n\n        <div class=\"eiev-rp-controls\">\n            <button type=\"button\" class=\"eiev-rp-back\" style=\"display:none;\">\u2039 Back<\/button>\n            <button type=\"button\" class=\"eiev-rp-refresh\">Refresh<\/button>\n            <button type=\"button\" class=\"eiev-rp-json-main\">JSON<\/button>\n        <\/div>\n\n        <div class=\"eiev-rp-error\" style=\"display:none;\"><\/div>\n        <main class=\"eiev-rp-content\"><\/main>\n        <pre class=\"eiev-rp-main-json\" style=\"display:none;\"><\/pre>\n    <\/div>\n\n    <script src=\"https:\/\/cdn.socket.io\/4.7.5\/socket.io.min.js\"><\/script>\n    <script>\n    (function(){\n        const root = document.getElementById('eiev_rp_743758');\n        const content = root.querySelector('.eiev-rp-content');\n        const statusBox = root.querySelector('.eiev-rp-status');\n        const errorBox = root.querySelector('.eiev-rp-error');\n        const backBtn = root.querySelector('.eiev-rp-back');\n        const refreshBtn = root.querySelector('.eiev-rp-refresh');\n        const jsonBtn = root.querySelector('.eiev-rp-json-main');\n        const jsonBox = root.querySelector('.eiev-rp-main-json');\n\n        const startEventId = root.dataset.eventId || '';\n        const startRaceId = root.dataset.raceId || '';\n        const fallbackRaceId = root.dataset.raceIdFallback || '';\n        const liveSocketEnabled = root.dataset.liveSocket !== 'no';\n\n        const API = {\n            events: \"https:\/\/eievss.com\/website\/wordpress\/wp-json\/eiev-results\/v1\/events\",\n            races: \"https:\/\/eievss.com\/website\/wordpress\/wp-json\/eiev-results\/v1\/races\/\",\n            race: \"https:\/\/eievss.com\/website\/wordpress\/wp-json\/eiev-results\/v1\/race\/\",\n            decryptSocket: \"https:\/\/eievss.com\/website\/wordpress\/wp-json\/eiev-results\/v1\/decrypt-socket\"\n        };\n\n        let view = 'events';\n        let currentEventId = '';\n        let currentRaceId = '';\n        let lastAction = null;\n        let lastData = null;\n        let socket = null;\n\n        function esc(value) {\n            return String(value ?? '')\n                .replace(\/&\/g,'&amp;')\n                .replace(\/<\/g,'&lt;')\n                .replace(\/>\/g,'&gt;')\n                .replace(\/\"\/g,'&quot;')\n                .replace(\/'\/g,'&#039;');\n        }\n\n        function val(obj, keys, fallback='') {\n            for (const key of keys) {\n                if (obj && obj[key] !== undefined && obj[key] !== null && String(obj[key]) !== '') {\n                    return obj[key];\n                }\n            }\n            return fallback;\n        }\n\n        function asArray(data) {\n            if (Array.isArray(data)) return data;\n            if (!data || typeof data !== 'object') return [];\n            for (const key of ['data','events','races','items','records','result','list']) {\n                if (Array.isArray(data[key])) return data[key];\n            }\n            for (const key in data) {\n                if (Array.isArray(data[key])) return data[key];\n            }\n            return [];\n        }\n\n        function setStatus(text, mode = '') {\n            statusBox.textContent = text;\n            root.classList.remove('is-loading','is-error','is-ok','is-live');\n            if (mode) root.classList.add('is-' + mode);\n        }\n\n        function showError(message) {\n            errorBox.style.display = 'block';\n            errorBox.textContent = message;\n        }\n\n        function clearError() {\n            errorBox.style.display = 'none';\n            errorBox.textContent = '';\n        }\n\n        function formatDate(value) {\n            if (!value) return '';\n            const d = new Date(String(value).replace(' ', 'T'));\n            if (isNaN(d.getTime())) return String(value);\n            return d.toLocaleDateString('en-GB', { day:'2-digit', month:'short', year:'numeric' });\n        }\n\n        function formatDateTime(value) {\n            if (!value) return '';\n            const d = new Date(String(value).replace(' ', 'T'));\n            if (isNaN(d.getTime())) return String(value);\n            return d.toLocaleDateString('en-GB', { day:'2-digit', month:'short', year:'numeric' }) + ' ' +\n                   d.toLocaleTimeString('en-GB', { hour:'2-digit', minute:'2-digit' });\n        }\n\n        function statusClass(status) {\n            const s = String(status || '').toLowerCase();\n            if (s.includes('live') || s.includes('arrived') || s.includes('onloop')) return 'live';\n            if (s.includes('upcoming')) return 'upcoming';\n            if (s.includes('finish') || s === 'unknown') return 'finished';\n            if (s.includes('eliminated') || s.includes('failed')) return 'eliminated';\n            return 'finished';\n        }\n\n        function parseMaybeJson(value) {\n            if (typeof value !== 'string') return value;\n            const trimmed = value.trim();\n            if (!trimmed || (trimmed[0] !== '{' && trimmed[0] !== '[')) return value;\n            try { return JSON.parse(trimmed); } catch(e) { return value; }\n        }\n\n        function normalizeApiData(data) {\n            \/\/ Accept all possible shapes returned by WP REST and the encrypted API.\n            let out = parseMaybeJson(data);\n            let guard = 0;\n            while (out && typeof out === 'object' && !Array.isArray(out) && guard < 10) {\n                out = parseMaybeJson(out);\n                if (out.success !== undefined && out.data !== undefined) {\n                    out = parseMaybeJson(out.data);\n                    guard++;\n                    continue;\n                }\n                if (out.data !== undefined && Object.keys(out).length === 1) {\n                    out = parseMaybeJson(out.data);\n                    guard++;\n                    continue;\n                }\n                break;\n            }\n            return out;\n        }\n\n        async function api(url) {\n            clearError();\n            setStatus('Loading...', 'loading');\n\n            const response = await fetch(url, { credentials: 'same-origin' });\n            const json = await response.json();\n\n            lastData = json;\n            jsonBox.textContent = JSON.stringify(json, null, 2);\n\n            if (!json.success) {\n                throw new Error(json.message || 'API error');\n            }\n\n            setStatus('Loaded', 'ok');\n            return normalizeApiData(json.data);\n        }\n\n        function eventId(item) {\n            return val(item, ['id','event_id','web_event_id','eventId'], '');\n        }\n\n        function raceId(item) {\n            const id = val(item, ['race_code','id','race_id','web_race_id','raceId'], '');\n            if (id) return id;\n\n            \/\/ The race list JSON can return race_code:null. Use fallback when configured.\n            return fallbackRaceId || '';\n        }\n\n        async function loadEvents() {\n            try {\n                stopSocket();\n                view = 'events';\n                currentEventId = '';\n                currentRaceId = '';\n                backBtn.style.display = 'none';\n                lastAction = loadEvents;\n\n                const data = await api(API.events);\n                renderEvents(asArray(data));\n            } catch (error) {\n                content.innerHTML = '';\n                setStatus('Error', 'error');\n                showError(error.message);\n            }\n        }\n\n        async function loadRaces(eventIdValue, label = 'Races') {\n            try {\n                stopSocket();\n                view = 'races';\n                currentEventId = eventIdValue;\n                currentRaceId = '';\n                backBtn.style.display = 'inline-flex';\n                lastAction = () => loadRaces(eventIdValue, label);\n\n                const data = await api(API.races + eventIdValue);\n                renderRaces(asArray(data), label);\n            } catch (error) {\n                content.innerHTML = '';\n                setStatus('Error', 'error');\n                showError(error.message);\n            }\n        }\n\n        async function loadRace(raceIdValue, label = 'Race Details') {\n            try {\n                if (!raceIdValue) {\n                    showError('Race details cannot be opened because the race list did not provide race_code. Add race_id_fallback=\"36735\" to the shortcode or ask the API provider to return race_code.');\n                    return;\n                }\n\n                view = 'race';\n                currentRaceId = raceIdValue;\n                backBtn.style.display = 'inline-flex';\n                lastAction = () => loadRace(raceIdValue, label);\n\n                const data = await api(API.race + raceIdValue);\n                renderRaceDetails(data, raceIdValue, label);\n                startSocket(raceIdValue);\n            } catch (error) {\n                content.innerHTML = '';\n                setStatus('Error', 'error');\n                showError(error.message);\n            }\n        }\n\n        function renderEvents(items) {\n            if (!items.length) {\n                content.innerHTML = '<div class=\"eiev-rp-empty\">No live or upcoming events returned by the API.<\/div>';\n                return;\n            }\n\n            content.innerHTML = `\n                <div class=\"eiev-rp-grid eiev-rp-events\">\n                    ${items.map(item => {\n                        const id = eventId(item);\n                        const name = val(item, ['name_en','name','event_name','title'], 'Event');\n                        const location = val(item, ['location','country'], '');\n                        const country = val(item, ['country','country_code'], '');\n                        const start = formatDate(val(item, ['start_date','event_start_time'], ''));\n                        const end = formatDate(val(item, ['end_date'], ''));\n                        const count = val(item, ['race_count'], 0);\n                        const status = val(item, ['event_current_status','status'], 'Event');\n                        const logo = item.organizer && item.organizer.logo ? item.organizer.logo : item.avatar;\n\n                        return `\n                            <article class=\"eiev-rp-event-card\" data-id=\"${esc(id)}\" data-title=\"${esc(name)}\">\n                                <div class=\"eiev-rp-card-top\">\n                                    <div class=\"eiev-rp-logo\">${logo ? `<img decoding=\"async\" src=\"${esc(logo)}\" alt=\"\">` : '\ud83c\udfc7'}<\/div>\n                                    <span class=\"eiev-rp-badge ${statusClass(status)}\">${esc(status)}<\/span>\n                                <\/div>\n                                <h2>${esc(name)}<\/h2>\n                                <div class=\"eiev-rp-muted\">\ud83d\udccd ${esc(location || country)}<\/div>\n                                <div class=\"eiev-rp-muted\">\ud83d\uddd3\ufe0f ${esc(start)}${end && end !== start ? ' - ' + esc(end) : ''}<\/div>\n                                <div class=\"eiev-rp-card-footer\">\n                                    <strong>${esc(count)} Races<\/strong>\n                                    <span>View races \u203a<\/span>\n                                <\/div>\n                            <\/article>\n                        `;\n                    }).join('')}\n                <\/div>\n            `;\n\n            content.querySelectorAll('.eiev-rp-event-card').forEach(card => {\n                card.addEventListener('click', function(){\n                    loadRaces(this.dataset.id, this.dataset.title);\n                });\n            });\n        }\n\n        function renderRaces(items, title) {\n            items = asArray(items).filter((race) => {\n                const status = String(val(race, ['race_status','status'], '') || '').toLowerCase().trim();\n\n                \/\/ Hide finished races. The API sometimes sends completed races as Unknown,\n                \/\/ empty status, Finished, Completed, Closed, Ended, Done, or Result.\n                if (!status) return false;\n                if (status === 'unknown') return false;\n                if (status.includes('finish')) return false;\n                if (status.includes('complete')) return false;\n                if (status.includes('closed')) return false;\n                if (status.includes('ended')) return false;\n                if (status.includes('done')) return false;\n                if (status.includes('result')) return false;\n\n                return true;\n            });\n\n            if (!items.length) {\n                content.innerHTML = '<div class=\"eiev-rp-empty\">No live or upcoming races found for this event.<\/div>';\n                return;\n            }\n\n            items = [...items].sort((a,b)=>{\n                const getPriority=(race)=>{\n                    const status=String(val(race,['race_status','status'],'')).toLowerCase();\n                    if(status.includes('live')) return 0;\n                    if(status.includes('upcoming')) return 1;\n                    return 2;\n                };\n                return getPriority(a)-getPriority(b);\n            });\n\n            const totalStart = items.reduce((sum, item) => sum + Number(val(item, ['start_list_count'], 0)), 0);\n            const totalFinished = items.reduce((sum, item) => sum + Number(val(item, ['finished_count'], 0)), 0);\n            const totalEliminated = items.reduce((sum, item) => sum + Number(val(item, ['eliminated_count'], 0)), 0);\n\n            content.innerHTML = `\n                <div class=\"eiev-rp-section-title\">\n                    <div>\n                        <h2>${esc(title)}<\/h2>\n                        <p>Live and upcoming races for event ID ${esc(currentEventId)}<\/p>\n                    <\/div>\n                <\/div>\n\n                <div class=\"eiev-rp-summary\">\n                    <div><b>${items.length}<\/b><span>Races<\/span><\/div>\n                    <div><b>${totalStart}<\/b><span>Start List<\/span><\/div>\n                    <div><b>${totalEliminated}<\/b><span>Eliminated<\/span><\/div>\n                <\/div>\n\n                <div class=\"eiev-rp-race-list\">\n                    ${items.map((item, index) => {\n                        const id = raceId(item);\n                        const originalId = val(item, ['race_code','id','race_id','web_race_id','raceId'], '');\n                        const name = val(item, ['name','name_en','race_name','title','name_ar'], 'Race');\n                        const start = val(item, ['start_time','race_date','date'], '');\n                        let status = val(item, ['race_status','status'], 'Finished');\n                        if (!status || String(status).toLowerCase() === 'unknown') {\n                            status = 'Finished';\n                        }\n                        const qualifier = val(item, ['qualifier'], '-');\n                        const loops = val(item, ['loop_count'], '-');\n                        const startList = Number(val(item, ['start_list_count'], 0));\n                        const finished = Number(val(item, ['finished_count'], 0));\n                        const eliminated = Number(val(item, ['eliminated_count'], 0));\n                        const percent = startList > 0 ? Math.round((finished \/ startList) * 100) : 0;\n\n                        return `\n                            <article class=\"eiev-rp-race-card\" data-id=\"${esc(id)}\" data-title=\"${esc(name)}\">\n                                <div class=\"eiev-rp-race-number\">\n                                    <b>${esc(originalId || index + 1)}<\/b>\n                                    <span>${esc(loops)} Gates<\/span>\n                                <\/div>\n\n                                <div class=\"eiev-rp-race-main\">\n                                    <div class=\"eiev-rp-race-meta\">Qualifier ${esc(qualifier)} \u2022 ${esc(formatDateTime(start))}<\/div>\n                                    <h3>${esc(name)}<\/h3>\n                                    <div class=\"eiev-rp-progress\"><span style=\"width:${percent}%\"><\/span><\/div>\n\n                                    <div class=\"eiev-rp-mini-stats\">\n                                        <span><b>${esc(startList)}<\/b> Start<\/span>\n                                        <span><b>${esc(finished)}<\/b> Finished<\/span>\n                                        <span><b>${esc(eliminated)}<\/b> Eliminated<\/span>\n                                    <\/div>\n                                <\/div>\n\n                                <div class=\"eiev-rp-race-action\">\n                                    <span class=\"eiev-rp-badge ${statusClass(status)}\">${esc(status)}<\/span>\n                                    <span class=\"eiev-rp-arrow\">\u203a<\/span>\n                                <\/div>\n                            <\/article>\n                        `;\n                    }).join('')}\n                <\/div>\n            `;\n\n            content.querySelectorAll('.eiev-rp-race-card').forEach(card => {\n                card.addEventListener('click', function(){\n                    loadRace(this.dataset.id, this.dataset.title);\n                });\n            });\n        }\n\n        function looksLikeRider(item) {\n            if (!item || typeof item !== 'object' || Array.isArray(item)) return false;\n\n            const keys = Object.keys(item);\n            const lowerKeys = keys.map(k => String(k).toLowerCase());\n            const joined = lowerKeys.join('|');\n\n            \/\/ Avoid treating pure gate\/loop rows as riders.\n            const isOnlyGateLike = lowerKeys.length > 0 && lowerKeys.every(k =>\n                ['gate','gate_no','loop','loop_no','distance','total_distance','hold_time','max_heart_rate','color','arrival_time','inspection_time','departure','recovery','heart_rate','avg_speed','speed','status'].includes(k)\n            );\n            if (isOnlyGateLike) return false;\n\n            const strongKeys = [\n                'rider_name','ridername','rider_full_name','athlete_name','athletename','competitor_name','participant_name',\n                'horse_name','horsename','start_no','start_number','bib','bib_no','rider_fei_id','horse_fei_id',\n                'athlete_status','final_pos','final_position','rank','place','position'\n            ];\n            if (strongKeys.some(k => lowerKeys.includes(k))) return true;\n\n            \/\/ Finished-race result rows sometimes use nested rider\/horse objects or IDs.\n            if ((joined.includes('rider') || joined.includes('athlete') || joined.includes('horse')) &&\n                (joined.includes('name') || joined.includes('id') || joined.includes('fei') || joined.includes('start') || joined.includes('position') || joined.includes('rank'))) {\n                return true;\n            }\n\n            \/\/ Some APIs return rider as { rider:{...}, horse:{...}, gates:[...] }.\n            if ((item.rider && typeof item.rider === 'object') || (item.horse && typeof item.horse === 'object')) return true;\n\n            return false;\n        }\n\n        function normalizeRider(item) {\n            if (!item || typeof item !== 'object') return item;\n            const out = {...item};\n\n            const riderObj = parseMaybeJson(out.rider || out.Rider || out.athlete || out.Athlete || out.competitor || out.Competitor);\n            const horseObj = parseMaybeJson(out.horse || out.Horse);\n\n            if (riderObj && typeof riderObj === 'object' && !Array.isArray(riderObj)) {\n                Object.keys(riderObj).forEach(key => {\n                    if (out[key] === undefined) out[key] = riderObj[key];\n                    const prefixed = 'rider_' + key;\n                    if (out[prefixed] === undefined) out[prefixed] = riderObj[key];\n                });\n            }\n\n            if (horseObj && typeof horseObj === 'object' && !Array.isArray(horseObj)) {\n                Object.keys(horseObj).forEach(key => {\n                    if (out[key] === undefined) out[key] = horseObj[key];\n                    const prefixed = 'horse_' + key;\n                    if (out[prefixed] === undefined) out[prefixed] = horseObj[key];\n                });\n            }\n\n            out.rider_name = val(out, ['rider_name','riderName','rider_full_name','athlete_name','athleteName','competitor_name','participant_name','name_en','name'], out.rider_name || '');\n            out.horse_name = val(out, ['horse_name','horseName','horse_full_name','horse_en','horse'], (horseObj && val(horseObj, ['name','name_en','horse_name'], '')) || out.horse_name || '');\n            out.start_no = val(out, ['start_no','startNo','start_number','bib','bib_no','number'], out.start_no || '');\n            out.rider_natio = val(out, ['rider_natio','riderNationality','nationality','country','natio'], out.rider_natio || '');\n            out.athlete_status = val(out, ['athlete_status','athleteStatus','result_status','resultStatus','status'], out.athlete_status || '');\n            out.final_pos = val(out, ['final_pos','finalPos','final_position','position','rank','place','pos'], out.final_pos || '');\n            out.final_avg_speed = val(out, ['final_avg_speed','finalAvgSpeed','avg_speed','speed'], out.final_avg_speed || '');\n            out.rider_fei_id = val(out, ['rider_fei_id','riderFeiId','athlete_fei_id','fei_id'], out.rider_fei_id || '');\n            out.horse_fei_id = val(out, ['horse_fei_id','horseFeiId'], out.horse_fei_id || '');\n\n            const gates = val(out, ['rider_race_gates','riderRaceGates','gates','race_gates','loops','vet_gates'], null);\n            if (Array.isArray(gates) && !Array.isArray(out.rider_race_gates)) out.rider_race_gates = gates;\n\n            return out;\n        }\n\n        function riderArrayScore(arr) {\n            if (!Array.isArray(arr) || !arr.length) return 0;\n            let score = 0;\n            arr.forEach(item => {\n                if (looksLikeRider(item)) score += 10;\n                if (item && typeof item === 'object') {\n                    const keys = Object.keys(item).join('|').toLowerCase();\n                    if (keys.includes('rider')) score += 3;\n                    if (keys.includes('horse')) score += 3;\n                    if (keys.includes('start')) score += 2;\n                    if (keys.includes('position') || keys.includes('rank') || keys.includes('final')) score += 2;\n                }\n            });\n            return score;\n        }\n\n        function findRiders(data, seen = new WeakSet()) {\n            data = normalizeApiData(data);\n            if (!data) return [];\n\n            if (Array.isArray(data)) {\n                const directScore = riderArrayScore(data);\n                if (directScore > 0) return data.filter(looksLikeRider).map(normalizeRider);\n\n                let best = [];\n                let bestScore = 0;\n                for (const item of data) {\n                    if (item && typeof item === 'object') {\n                        const found = findRiders(item, seen);\n                        const score = riderArrayScore(found);\n                        if (score > bestScore) {\n                            best = found;\n                            bestScore = score;\n                        }\n                    }\n                }\n                return best;\n            }\n\n            if (typeof data !== 'object') return [];\n            if (seen.has(data)) return [];\n            seen.add(data);\n\n            const directKeys = [\n                'riders','Riders','rider','Rider',\n                'results','Results','race_results','raceResults','classification','classifications','ranking','rankings',\n                'rider_gate_information','riderGateInformation','rider_gate_info','riderGateInfo',\n                'participants','Participants','entries','Entries','entry_list','entryList',\n                'start_list','startList','startlist','StartList',\n                'athletes','Athletes','competitors','Competitors','starters','Starters'\n            ];\n\n            let best = [];\n            let bestScore = 0;\n\n            function consider(value) {\n                value = parseMaybeJson(value);\n                if (Array.isArray(value)) {\n                    const direct = value.filter(looksLikeRider).map(normalizeRider);\n                    const directScore = riderArrayScore(direct);\n                    if (directScore > bestScore) {\n                        best = direct;\n                        bestScore = directScore;\n                    }\n                    for (const item of value) {\n                        if (item && typeof item === 'object') {\n                            const nested = findRiders(item, seen);\n                            const nestedScore = riderArrayScore(nested);\n                            if (nestedScore > bestScore) {\n                                best = nested;\n                                bestScore = nestedScore;\n                            }\n                        }\n                    }\n                } else if (value && typeof value === 'object') {\n                    const nested = findRiders(value, seen);\n                    const nestedScore = riderArrayScore(nested);\n                    if (nestedScore > bestScore) {\n                        best = nested;\n                        bestScore = nestedScore;\n                    }\n                }\n            }\n\n            for (const key of directKeys) consider(data[key]);\n            for (const key in data) consider(data[key]);\n\n            return best;\n        }\n\n        function latestGate(rider) {\n            const gates = rider ? (Array.isArray(rider.rider_race_gates) ? rider.rider_race_gates : (Array.isArray(rider.riderRaceGates) ? rider.riderRaceGates : (Array.isArray(rider.gates) ? rider.gates : (Array.isArray(rider.loops) ? rider.loops : [])))) : [];\n            let latest = null;\n\n            gates.forEach(gate => {\n                const hasData = gate.arrival_time !== '-' || gate.inspection_time !== '-' || gate.departure !== '-';\n                if (hasData) latest = gate;\n            });\n\n            return latest || gates[0] || {};\n        }\n\n        function riderPositionValue(rider) {\n            const raw = val(rider, ['final_pos','position','pos','rank','place','serial'], '');\n            const match = String(raw || '').match(\/\\d+\/);\n            return match ? Number(match[0]) : Number.POSITIVE_INFINITY;\n        }\n\n        function sortRidersByPosition(riders) {\n            return [...riders].sort((a, b) => {\n                const pa = riderPositionValue(a);\n                const pb = riderPositionValue(b);\n                if (pa !== pb) return pa - pb;\n\n                const sa = Number(String(val(a, ['start_no'], '999999')).replace(\/\\D\/g, '') || 999999);\n                const sb = Number(String(val(b, ['start_no'], '999999')).replace(\/\\D\/g, '') || 999999);\n                return sa - sb;\n            });\n        }\n\n        function renderInfoRows(rows) {\n            return rows\n                .filter(row => row[1] !== undefined && row[1] !== null && String(row[1]).trim() !== '')\n                .map(row => `<div class=\"eiev-rp-info-row\"><span>${esc(row[0])}<\/span><b>${esc(row[1])}<\/b><\/div>`)\n                .join('');\n        }\n\n        function renderGatesInfo(rider) {\n            const gates = rider ? (Array.isArray(rider.rider_race_gates) ? rider.rider_race_gates : (Array.isArray(rider.riderRaceGates) ? rider.riderRaceGates : (Array.isArray(rider.gates) ? rider.gates : (Array.isArray(rider.loops) ? rider.loops : [])))) : [];\n            if (!gates.length) return '';\n\n            return `\n                <h4>Gate Information<\/h4>\n                <div class=\"eiev-rp-info-gates\">\n                    ${gates.map(gate => `\n                        <div class=\"eiev-rp-info-gate\">\n                            <b>Gate ${esc(val(gate, ['gate','gate_no','loop'], '-'))}<\/b>\n                            <small>Arrival: ${esc(val(gate, ['arrival_time','arrival'], '-'))}<\/small>\n                            <small>Inspection: ${esc(val(gate, ['inspection_time','invet'], '-'))}<\/small>\n                            <small>Recovery: ${esc(val(gate, ['recovery'], '-'))}<\/small>\n                            <small>HR: ${esc(val(gate, ['heart_rate','hr'], '-'))}<\/small>\n                            <small>Speed: ${esc(val(gate, ['avg_speed','speed'], '-'))}<\/small>\n                        <\/div>\n                    `).join('')}\n                <\/div>\n            `;\n        }\n\n        function showRiderHorseInfo(rider, type) {\n            const modal = content.querySelector('.eiev-rp-info-modal');\n            if (!modal) return;\n\n            const gate = latestGate(rider);\n            const title = type === 'horse'\n                ? val(rider, ['horse_name'], 'Horse Information')\n                : val(rider, ['rider_name','name'], 'Rider Information');\n\n            const riderRows = [\n                ['Start No.', val(rider, ['start_no'], '')],\n                ['Position', val(rider, ['final_pos','position','pos','rank','place','serial'], '')],\n                ['Rider Name', val(rider, ['rider_name','name'], '')],\n                ['Rider FEI ID', val(rider, ['rider_fei_id'], '')],\n                ['Rider EEF ID', val(rider, ['rider_eef_id'], '')],\n                ['Nationality', val(rider, ['rider_natio','nationality'], '')],\n                ['Stable', val(rider, ['stable_name'], '')],\n                ['Status', val(rider, ['athlete_status','race_status','status'], '')],\n                ['Final Speed', val(rider, ['final_avg_speed'], '') || val(gate, ['avg_speed','speed'], '')],\n                ['Recovery', val(gate, ['recovery'], '')],\n                ['Heart Rate', val(gate, ['heart_rate','hr'], '')]\n            ];\n\n            const horseRows = [\n                ['Horse Name', val(rider, ['horse_name'], '')],\n                ['Horse FEI ID', val(rider, ['horse_fei_id'], '')],\n                ['Horse EEF ID', val(rider, ['horse_eef_id'], '')],\n                ['Age', val(rider, ['horse_age'], '')],\n                ['Color', val(rider, ['horse_color'], '')],\n                ['Gender', val(rider, ['horse_gender'], '')],\n                ['Breed', val(rider, ['horse_breed'], '')],\n                ['Owner', val(rider, ['owner_name','horse_owner'], '')],\n                ['Trainer', val(rider, ['trainer_name'], '')]\n            ];\n\n            modal.innerHTML = `\n                <div class=\"eiev-rp-info-backdrop\" data-close=\"1\"><\/div>\n                <div class=\"eiev-rp-info-card\" role=\"dialog\" aria-modal=\"true\">\n                    <button type=\"button\" class=\"eiev-rp-info-close\" data-close=\"1\">\u00d7<\/button>\n                    <span class=\"eiev-rp-info-label\">${type === 'horse' ? 'Horse' : 'Rider'} Information<\/span>\n                    <h3>${esc(title)}<\/h3>\n                    <div class=\"eiev-rp-info-grid\">\n                        <section>\n                            <h4>Rider<\/h4>\n                            ${renderInfoRows(riderRows) || '<p>No rider details available.<\/p>'}\n                        <\/section>\n                        <section>\n                            <h4>Horse<\/h4>\n                            ${renderInfoRows(horseRows) || '<p>No horse details available.<\/p>'}\n                        <\/section>\n                    <\/div>\n                    ${renderGatesInfo(rider)}\n                <\/div>\n            `;\n            modal.style.display = 'block';\n            modal.querySelectorAll('[data-close]').forEach(el => {\n                el.addEventListener('click', () => {\n                    modal.style.display = 'none';\n                    modal.innerHTML = '';\n                });\n            });\n        }\n\n\n        function renderRaceDetails(data, raceIdValue, title) {\n            data = normalizeApiData(data);\n            const race = Array.isArray(data) ? (data.find(item => item && typeof item === 'object' && !looksLikeRider(item)) || data[0] || {}) : (data && typeof data === 'object' ? data : {});\n            const riders = sortRidersByPosition(findRiders(data));\n            const gates = Array.isArray(race.racegates) ? race.racegates : (Array.isArray(race.gates) ? race.gates : []);\n            const raceTitle = val(race, ['name','name_en','race_name','title'], title);\n            let status = val(race, ['race_status','status'], 'Finished');\n            if (!status || String(status).toLowerCase() === 'unknown') {\n                status = 'Finished';\n            }\n            const start = val(race, ['start_time'], '');\n\n            content.innerHTML = `\n                <div class=\"eiev-rp-detail-head\">\n                    <div>\n                        <span>Race ID #${esc(raceIdValue)}<\/span>\n                        <h2>${esc(raceTitle)}<\/h2>\n                        <p>${esc(formatDateTime(start))}<\/p>\n                    <\/div>\n                    <span class=\"eiev-rp-badge ${statusClass(status)}\">${esc(status)}<\/span>\n                <\/div>\n\n                <div class=\"eiev-rp-summary\">\n                    <div><b>${esc(val(race, ['start_list_count'], riders.length))}<\/b><span>Start List<\/span><\/div>\n                    <div><b>${esc(val(race, ['finished_count'], 0))}<\/b><span>Finished<\/span><\/div>\n                    <div><b>${esc(val(race, ['eliminated_count'], 0))}<\/b><span>Eliminated<\/span><\/div>\n                    <div><b>${esc(val(race, ['loop_count'], gates.length))}<\/b><span>Gates<\/span><\/div>\n                <\/div>\n\n                ${gates.length ? `\n                    <h3 class=\"eiev-rp-subtitle\">Race Gates<\/h3>\n                    <div class=\"eiev-rp-gates\">\n                        ${gates.map(g => `\n                            <div class=\"eiev-rp-gate\">\n                                <div class=\"eiev-rp-gate-color\" style=\"background:${esc(String(g.color || '#ddd').toLowerCase())}\"><\/div>\n                                <b>Gate ${esc(g.gate)}<\/b>\n                                <span>${esc(g.distance)} km<\/span>\n                                <small>Total ${esc(g.total_distance)} km<\/small>\n                                <small>Hold ${esc(g.hold_time)} min<\/small>\n                                <small>HR ${esc(g.max_heart_rate)}<\/small>\n                            <\/div>\n                        `).join('')}\n                    <\/div>\n                ` : ''}\n\n                <div class=\"eiev-rp-riders-head\">\n                    <h3>Riders<\/h3>\n                    <input type=\"search\" class=\"eiev-rp-search\" placeholder=\"Search rider or horse...\">\n                <\/div>\n\n                ${riders.length ? `\n                    <div class=\"eiev-rp-table-wrap\">\n                        <table class=\"eiev-rp-riders-table\">\n                            <thead>\n                                <tr>\n                                    <th>Pos<\/th>\n                                    <th>No.<\/th>\n                                    <th>Rider<\/th>\n                                    <th>Horse<\/th>\n                                    <th>Nation<\/th>\n                                    <th>Status<\/th>\n                                    <th>Speed<\/th>\n                                    <th>Recovery<\/th>\n                                    <th>HR<\/th>\n                                <\/tr>\n                            <\/thead>\n                            <tbody>\n                                ${riders.map((rider, index) => {\n                                    const gate = latestGate(rider);\n                                    const finalPos = val(rider, ['final_pos','position','pos','rank','place','serial'], index + 1);\n                                    const status = val(rider, ['athlete_status','race_status','status'], '-');\n                                    return `\n                                        <tr data-rider-index=\"${index}\">\n                                            <td>${esc(finalPos)}<\/td>\n                                            <td>${esc(val(rider, ['start_no'], '-'))}<\/td>\n                                            <td>\n                                                <button type=\"button\" class=\"eiev-rp-link-btn eiev-rp-rider-open\" data-rider-index=\"${index}\" data-info-type=\"rider\">\n                                                    <strong>${esc(val(rider, ['rider_name','name'], '-'))}<\/strong>\n                                                    <small>${esc(val(rider, ['rider_fei_id'], ''))}<\/small>\n                                                <\/button>\n                                            <\/td>\n                                            <td>\n                                                <button type=\"button\" class=\"eiev-rp-link-btn eiev-rp-horse-open\" data-rider-index=\"${index}\" data-info-type=\"horse\">\n                                                    <strong>${esc(val(rider, ['horse_name'], '-'))}<\/strong>\n                                                    <small>${esc(val(rider, ['horse_age'], ''))} yrs \u2022 ${esc(val(rider, ['horse_color'], ''))}<\/small>\n                                                <\/button>\n                                            <\/td>\n                                            <td>${esc(val(rider, ['rider_natio','stable_name'], '-'))}<\/td>\n                                            <td><span class=\"eiev-rp-badge ${statusClass(status)}\">${esc(status)}<\/span><\/td>\n                                            <td>${esc(val(rider, ['final_avg_speed'], '') || val(gate, ['avg_speed','speed'], '-'))}<\/td>\n                                            <td>${esc(val(gate, ['recovery'], '-'))}<\/td>\n                                            <td>${esc(val(gate, ['heart_rate'], '-'))}<\/td>\n                                        <\/tr>\n                                    `;\n                                }).join('')}\n                            <\/tbody>\n                        <\/table>\n                    <\/div>\n                ` : `<div class=\"eiev-rp-empty\">Race loaded, but no riders were found.<br><small>Debug: keys = ${esc(Object.keys(race || {}).join(', '))}<\/small><\/div>`}\n\n                <div class=\"eiev-rp-info-modal\" style=\"display:none;\"><\/div>\n\n                <button type=\"button\" class=\"eiev-rp-show-json\">Show JSON<\/button>\n                <pre class=\"eiev-rp-json\" style=\"display:none;\">${esc(JSON.stringify(data, null, 2))}<\/pre>\n            `;\n\n            const search = content.querySelector('.eiev-rp-search');\n            if (search) {\n                search.addEventListener('input', function(){\n                    const q = this.value.toLowerCase();\n                    content.querySelectorAll('.eiev-rp-riders-table tbody tr').forEach(row => {\n                        row.style.display = row.textContent.toLowerCase().includes(q) ? '' : 'none';\n                    });\n                });\n            }\n\n            content.querySelectorAll('.eiev-rp-rider-open, .eiev-rp-horse-open').forEach(button => {\n                button.addEventListener('click', function(event){\n                    event.preventDefault();\n                    event.stopPropagation();\n                    const index = Number(this.dataset.riderIndex);\n                    const type = this.dataset.infoType || 'rider';\n                    if (!Number.isNaN(index) && riders[index]) {\n                        showRiderHorseInfo(riders[index], type);\n                    }\n                });\n            });\n\n            const btn = content.querySelector('.eiev-rp-show-json');\n            const pre = content.querySelector('.eiev-rp-json');\n            if (btn && pre) {\n                btn.addEventListener('click', function(){\n                    const visible = pre.style.display !== 'none';\n                    pre.style.display = visible ? 'none' : 'block';\n                    btn.textContent = visible ? 'Show JSON' : 'Hide JSON';\n                });\n            }\n        }\n\n        function startSocket(raceIdValue) {\n            stopSocket();\n\n            if (!liveSocketEnabled || typeof io === 'undefined') return;\n\n            try {\n                socket = io(\"https:\/\/socket.eiev-app.ae\", { transports: [\"websocket\"] });\n\n                socket.on(\"connect\", () => {\n                    setStatus('Live', 'live');\n                });\n\n                socket.on(\"race_update\", async payload => {\n                    try {\n                        const response = await fetch(API.decryptSocket, {\n                            method: 'POST',\n                            headers: { 'Content-Type': 'application\/json' },\n                            body: JSON.stringify(payload)\n                        });\n\n                        const json = await response.json();\n                        if (!json.success) return;\n\n                        const update = normalizeApiData(json.data);\n                        const updateId = update.id || update.raceId || update.race_id || (update.data && (update.data.id || update.data.raceId || update.data.race_id));\n                        if (String(updateId) !== String(raceIdValue)) return;\n\n                        setStatus('Updated', 'live');\n\n                        const updatedRace = normalizeApiData(update.data || update);\n                        if (updatedRace) {\n                            renderRaceDetails(updatedRace, raceIdValue, val(updatedRace, ['name'], 'Race Details'));\n                        }\n                    } catch(e) {\n                        console.error(e);\n                    }\n                });\n            } catch(e) {\n                console.error(e);\n            }\n        }\n\n        function stopSocket() {\n            if (socket) {\n                socket.disconnect();\n                socket = null;\n            }\n        }\n\n        backBtn.addEventListener('click', function(){\n            if (view === 'race') {\n                loadRaces(currentEventId, 'Races');\n            } else {\n                loadEvents();\n            }\n        });\n\n        refreshBtn.addEventListener('click', function(){\n            if (lastAction) lastAction();\n        });\n\n        jsonBtn.addEventListener('click', function(){\n            const visible = jsonBox.style.display !== 'none';\n            jsonBox.style.display = visible ? 'none' : 'block';\n            jsonBtn.textContent = visible ? 'JSON' : 'Hide JSON';\n        });\n\n        if (startRaceId) {\n            loadRace(startRaceId, 'Race Details');\n        } else if (startEventId) {\n            loadRaces(startEventId, 'Races');\n        } else {\n            loadEvents();\n        }\n    })();\n    <\/script>\n    <\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"page-builder","ast-site-content-layout":"full-width-container","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"pgc_sgb_lightbox_settings":"","footnotes":""},"class_list":["post-6424","page","type-page","status-publish","hentry"],"_hostinger_reach_plugin_has_subscription_block":false,"_hostinger_reach_plugin_is_elementor":false,"_links":{"self":[{"href":"https:\/\/eievss.com\/website\/wordpress\/wp-json\/wp\/v2\/pages\/6424","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/eievss.com\/website\/wordpress\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/eievss.com\/website\/wordpress\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/eievss.com\/website\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/eievss.com\/website\/wordpress\/wp-json\/wp\/v2\/comments?post=6424"}],"version-history":[{"count":7,"href":"https:\/\/eievss.com\/website\/wordpress\/wp-json\/wp\/v2\/pages\/6424\/revisions"}],"predecessor-version":[{"id":7091,"href":"https:\/\/eievss.com\/website\/wordpress\/wp-json\/wp\/v2\/pages\/6424\/revisions\/7091"}],"wp:attachment":[{"href":"https:\/\/eievss.com\/website\/wordpress\/wp-json\/wp\/v2\/media?parent=6424"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}