{"id":1432,"date":"2026-02-09T21:32:44","date_gmt":"2026-02-09T20:32:44","guid":{"rendered":"https:\/\/origin.yoomedoo.com\/?page_id=1432"},"modified":"2026-02-12T22:47:32","modified_gmt":"2026-02-12T21:47:32","slug":"ranking","status":"publish","type":"page","link":"https:\/\/origin.yoomedoo.com\/en\/ranking\/","title":{"rendered":"Ranking"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"1432\" class=\"elementor elementor-1432\">\n\t\t\t\t<div class=\"elementor-element elementor-element-39bdcbb2 e-flex e-con-boxed e-con e-parent\" data-id=\"39bdcbb2\" 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-f86637d elementor-widget elementor-widget-shortcode\" data-id=\"f86637d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\">    <style>\r\n        #max-speed-search-controls { display: flex; gap: 1em; align-items: center; flex-wrap: nowrap; margin-bottom: 1em; }\r\n        #max-speed-search-controls input,\r\n        #max-speed-search-controls button,\r\n        #max-speed-search-controls select { padding: 0.3em; }\r\n        .max-speed-narrow-select { max-width: 90px; }\r\n        .max-speed-medium-select { max-width: 140px; }\r\n        #max-speed-search-input { flex: 1 1 auto; min-width: 260px; }\r\n        #max-speed-table-container { width: 100%; }\r\n        .max-speed-table { width: 100%; border-collapse: collapse; }\r\n        .max-speed-table th, .max-speed-table td { padding: 0.5em; border-bottom: 1px solid #ddd; vertical-align: top; text-align: left; }\r\n        .max-speed-table td { word-break: break-word; overflow-wrap: anywhere; }\r\n\r\n        \/* Mobile: stack rows with labels so all columns are visible *\/\r\n        @media (max-width: 600px) {\r\n            #max-speed-search-controls { flex-direction: column; align-items: stretch; gap: 0.6em; padding: 0.25em 0; }\r\n            #max-speed-search-input { min-width: 0; width: 100%; }\r\n            #max-speed-search-btn { width: 100%; padding: 0.45em 1em; }\r\n            #max-speed-unit-select, #max-speed-age-select, #max-speed-category-select, #max-speed-birth-year-from, #max-speed-birth-year-to, .max-speed-narrow-select, .max-speed-medium-select { width: 100% !important; max-width: 100% !important; }\r\n            .max-speed-table thead { display: none; }\r\n            .max-speed-table, .max-speed-table tbody, .max-speed-table tr, .max-speed-table td { display: block; width: 100%; }\r\n            .max-speed-table tr { border: 1px solid #ddd; border-radius: 4px; margin-bottom: 0.75em; padding: 0.25em 0.5em; }\r\n            .max-speed-table td { border: none; display: flex; justify-content: space-between; gap: 1em; padding: 0.35em 0; }\r\n            .max-speed-table td::before { content: attr(data-label); font-weight: 600; flex: 0 0 42%; }\r\n        }\r\n    <\/style>\r\n\r\n    <div id=\"max-speed-search-controls\">\r\n        <input type=\"text\" id=\"max-speed-search-input\" placeholder=\"Buscar jugador...\" \/>\r\n        <select id=\"max-speed-age-select\" class=\"max-speed-narrow-select\">\r\n            <option value=\"\">Edad<\/option>\r\n        <\/select>\r\n        <select id=\"max-speed-category-select\" class=\"max-speed-medium-select\">\r\n            <option value=\"\">Categor\u00eda<\/option>\r\n            <option value=\"u11\">U11<\/option>\r\n            <option value=\"u12\">U12<\/option>\r\n            <option value=\"u13\">U13<\/option>\r\n            <option value=\"u14\">U14<\/option>\r\n            <option value=\"u15\">U15<\/option>\r\n            <option value=\"u16\">U16<\/option>\r\n            <option value=\"u17\">U17<\/option>\r\n            <option value=\"u18\">U18<\/option>\r\n            <option value=\"u19\">U19<\/option>\r\n            <option value=\"u20\">U20<\/option>\r\n            <option value=\"u21\">U21<\/option>\r\n            <option value=\"u23\">U23<\/option>\r\n            <option value=\"u23plus\">23+<\/option>\r\n        <\/select>\r\n        <select id=\"max-speed-birth-year-from\" class=\"max-speed-medium-select\">\r\n            <option value=\"\">Desde<\/option>\r\n        <\/select>\r\n        <select id=\"max-speed-birth-year-to\" class=\"max-speed-medium-select\">\r\n            <option value=\"\">Hasta<\/option>\r\n        <\/select>\r\n        <select id=\"max-speed-unit-select\" class=\"max-speed-narrow-select\">\r\n            <option value=\"kmh\">km\/h<\/option>\r\n            <option value=\"ms\">m\/s<\/option>\r\n            <option value=\"ft\">ft\/s<\/option>\r\n        <\/select>\r\n        <button id=\"max-speed-search-btn\">Buscar<\/button>\r\n    <\/div>\r\n    <div id=\"max-speed-table-container\"><\/div>\r\n    <div id=\"max-speed-pagination\" style=\"margin-top: 1em; display: flex; gap: 0.5em; flex-wrap: wrap;\"><\/div>\r\n    <script>\r\n    (function(){\r\n        const API_BASE = \"https:\\\/\\\/origin.yoomedoo.com\\\/en\\\/wp-json\\\/my-api\\\/v1\\\/max-speed\";\r\n        let currentPage = 1;\r\n        let pageSize = 50;\r\n        let totalItems = 0;\r\n        let totalPages = 1;\r\n        let currentUnit = 'kmh';\r\n        let currentSearch = '';\r\n        let lastData = [];\r\n        let lastRenderedItems = [];\r\n        let lastRequestId = 0;\r\n        let currentAge = '';\r\n        let currentCategory = '';\r\n        let currentBirthYearFrom = '';\r\n        let currentBirthYearTo = '';\r\n        let rankAllItemsCache = null;\r\n        let ageOptionsInitialized = false;\r\n\r\n        function unitLabel(unit) {\r\n            if(unit === 'kmh') return 'km\/h';\r\n            if(unit === 'ms') return 'm\/s';\r\n            if(unit === 'ft') return 'ft\/s';\r\n            return unit;\r\n        }\r\n\r\n        function convertSpeed(val, from) {\r\n            if(val === null || val === undefined) return '';\r\n            if(from === 'kmh') return val;\r\n            if(from === 'ms') return (val \/ 3.6).toFixed(3);\r\n            if(from === 'ft') return (val * 0.911344).toFixed(3); \/\/ 1 km\/h = 0.911344 ft\/s\r\n            return val;\r\n        }\r\n\r\n        function applyAgeFilter(items) {\r\n            if(currentAge === '') return items;\r\n            return items.filter(function(item) {\r\n                return calculateAge(item.birthdate) == currentAge;\r\n            });\r\n        }\r\n\r\n        function applyMinAgeFilter(items) {\r\n            return items.filter(function(item) {\r\n                const age = calculateAge(item.birthdate);\r\n                return age !== '' && age >= 10;\r\n            });\r\n        }\r\n\r\n        function applyCategoryFilter(items) {\r\n            if (currentCategory === '') return items;\r\n\r\n            const categoryRanges = {\r\n                u11: { min: 10, max: 11 },\r\n                u12: { min: 11, max: 12 },\r\n                u13: { min: 12, max: 13 },\r\n                u14: { min: 13, max: 14 },\r\n                u15: { min: 14, max: 15 },\r\n                u16: { min: 15, max: 16 },\r\n                u17: { min: 16, max: 17 },\r\n                u18: { min: 17, max: 18 },\r\n                u19: { min: 18, max: 19 },\r\n                u20: { min: 19, max: 20 },\r\n                u21: { min: 20, max: 21 },\r\n                u23: { min: 21, max: 23 },\r\n                u23plus: { min: 23, max: null }\r\n            };\r\n\r\n            const selectedRange = categoryRanges[currentCategory];\r\n            if (!selectedRange) return items;\r\n\r\n            return items.filter(function(item) {\r\n                const age = calculateAge(item.birthdate);\r\n                if (age === '' || isNaN(age)) return false;\r\n                if (selectedRange.max === null) return age >= selectedRange.min;\r\n                return age >= selectedRange.min && age < selectedRange.max;\r\n            });\r\n        }\r\n\r\n        function getBirthYear(birthdayStr) {\r\n            if (!birthdayStr) return '';\r\n            const birthDate = new Date(birthdayStr);\r\n            if (isNaN(birthDate.getTime())) return '';\r\n            return birthDate.getFullYear();\r\n        }\r\n\r\n        function applyBirthYearFilter(items) {\r\n            const fromYear = currentBirthYearFrom === '' ? null : parseInt(currentBirthYearFrom, 10);\r\n            const toYear = currentBirthYearTo === '' ? null : parseInt(currentBirthYearTo, 10);\r\n            if (fromYear === null && toYear === null) return items;\r\n\r\n            const minYear = fromYear === null ? toYear : (toYear === null ? fromYear : Math.min(fromYear, toYear));\r\n            const maxYear = fromYear === null ? toYear : (toYear === null ? fromYear : Math.max(fromYear, toYear));\r\n\r\n            return items.filter(function(item) {\r\n                const year = getBirthYear(item.birthdate);\r\n                if (year === '') return false;\r\n                if (minYear !== null && year < minYear) return false;\r\n                if (maxYear !== null && year > maxYear) return false;\r\n                return true;\r\n            });\r\n        }\r\n\r\n\r\n        function fillAgeOptions(items) {\r\n            const ageSelect = document.getElementById('max-speed-age-select');\r\n            const selectedAge = currentAge || ageSelect.value;\r\n            const availableAges = new Set();\r\n\r\n            for (const item of (items || [])) {\r\n                const age = calculateAge(item.birthdate);\r\n                if (age !== '' && !isNaN(age) && age >= 10 && age <= 38) {\r\n                    availableAges.add(age);\r\n                }\r\n            }\r\n\r\n            const sortedAges = Array.from(availableAges).sort(function(a, b) { return a - b; });\r\n            let options = '<option value=\"\">Edad<\/option>';\r\n            for (const age of sortedAges) {\r\n                options += '<option value=\"' + age + '\">' + age + '<\/option>';\r\n            }\r\n            ageSelect.innerHTML = options;\r\n\r\n            if (selectedAge !== '' && sortedAges.indexOf(parseInt(selectedAge, 10)) !== -1) {\r\n                ageSelect.value = selectedAge;\r\n            } else {\r\n                ageSelect.value = '';\r\n            }\r\n        }\r\n\r\n        function fillBirthYearOptions() {\r\n            const fromSelect = document.getElementById('max-speed-birth-year-from');\r\n            const toSelect = document.getElementById('max-speed-birth-year-to');\r\n            const currentYear = new Date().getFullYear();\r\n            const minYear = currentYear - 38;\r\n            const maxYear = currentYear - 10;\r\n            let fromOptions = '<option value=\"\">Desde<\/option>';\r\n            let toOptions = '<option value=\"\">Hasta<\/option>';\r\n\r\n            for (let year = minYear; year <= maxYear; year++) {\r\n                fromOptions += '<option value=\"' + year + '\">' + year + '<\/option>';\r\n                toOptions += '<option value=\"' + year + '\">' + year + '<\/option>';\r\n            }\r\n\r\n            fromSelect.innerHTML = fromOptions;\r\n            toSelect.innerHTML = toOptions;\r\n        }\r\n\r\n\r\n        function renderTable(items) {\r\n            lastRenderedItems = items;\r\n            let html = '<table class=\"max-speed-table\">';\r\n            html += '<thead><tr>';\r\n            html += '<th style=\"text-align:left;\">Rank<\/th>';\r\n            html += '<th style=\"text-align:left;\">Nombre<\/th>';\r\n            html += '<th style=\"text-align:left;\">Edad<\/th>';\r\n            html += '<th style=\"text-align:left;\">Posici\u00f3n<\/th>';\r\n            html += '<th style=\"text-align:left;\">Equipo<\/th>';\r\n            html += '<th style=\"text-align:left;\">Velocidad M\u00e1xima ('+unitLabel(currentUnit)+')<\/th>';\r\n            html += '<\/tr><\/thead><tbody>';\r\n\r\n            if(items.length === 0) {\r\n                html += '<tr><td colspan=\"6\">No se encontraron datos.<\/td><\/tr>';\r\n            } else {\r\n                for(let i = 0; i < items.length; i++) {\r\n                    const item = items[i];\r\n                    const displayRank = ((currentPage - 1) * pageSize) + i + 1;\r\n                    html += '<tr>';\r\n                    html += '<td data-label=\"Rank\">'+displayRank+'<\/td>';\r\n                    html += '<td data-label=\"Nombre\">'+item.player_name+'<\/td>';\r\n                    html += '<td data-label=\"Edad\">'+calculateAge(item.birthdate)+'<\/td>';\r\n                    html += '<td data-label=\"Posici\u00f3n\">'+item.player_position+'<\/td>';\r\n                    html += '<td data-label=\"Equipo\">'+item.team_name+'<\/td>';\r\n                    html += '<td data-label=\"Velocidad M\u00e1xima ('+unitLabel(currentUnit)+')\">'+convertSpeed(item.max_speed_kmh, currentUnit)+'<\/td>';\r\n                    html += '<\/tr>';\r\n                }\r\n            }\r\n            html += '<\/tbody><\/table>';\r\n            document.getElementById('max-speed-table-container').innerHTML = html;\r\n        }\r\n\r\n        function calculateAge(birthdayStr) {\r\n            if (!birthdayStr) return '';\r\n            const birthDate = new Date(birthdayStr);\r\n            if (isNaN(birthDate.getTime())) return '';\r\n            const today = new Date();\r\n            let age = today.getFullYear() - birthDate.getFullYear();\r\n            const m = today.getMonth() - birthDate.getMonth();\r\n            if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {\r\n                age--;\r\n            }\r\n            return age;\r\n        }\r\n\r\n        function renderPagination() {\r\n            let html = '';\r\n            if(totalPages <= 1) return document.getElementById('max-speed-pagination').innerHTML = '';\r\n\r\n            const maxButtons = 5;\r\n            let start = Math.max(1, currentPage - Math.floor(maxButtons \/ 2));\r\n            let end = start + maxButtons - 1;\r\n            if(end > totalPages) {\r\n                end = totalPages;\r\n                start = Math.max(1, end - maxButtons + 1);\r\n            }\r\n\r\n            if(currentPage > 1) {\r\n                html += '<button class=\"max-speed-page-btn\" data-page=\"'+(currentPage-1)+'\">Prev<\/button>';\r\n            }\r\n            for(let i=start; i<=end; i++) {\r\n                if(i === currentPage) {\r\n                    html += '<button class=\"max-speed-page-btn active\" data-page=\"'+i+'\" style=\"background:#0073e6;color:#fff;border:none;\">'+i+'<\/button>';\r\n                } else {\r\n                    html += '<button class=\"max-speed-page-btn\" data-page=\"'+i+'\">'+i+'<\/button>';\r\n                }\r\n            }\r\n            if(currentPage < totalPages) {\r\n                html += '<button class=\"max-speed-page-btn\" data-page=\"'+(currentPage+1)+'\">Next<\/button>';\r\n            }\r\n            document.getElementById('max-speed-pagination').innerHTML = html;\r\n            document.querySelectorAll('.max-speed-page-btn').forEach(btn => {\r\n                btn.onclick = function(){\r\n                    currentPage = parseInt(this.getAttribute('data-page'), 10);\r\n                    fetchData();\r\n                };\r\n            });\r\n        }\r\n\r\n        function fetchAllRankItems(ts) {\r\n            if (rankAllItemsCache) {\r\n                return Promise.resolve(rankAllItemsCache);\r\n            }\r\n\r\n            const firstUrl = API_BASE + '?' + new URLSearchParams({\r\n                page: '1',\r\n                page_size: String(pageSize),\r\n                _ts: String(ts)\r\n            }).toString();\r\n\r\n            return fetch(firstUrl, { cache: 'no-store' })\r\n                .then(r => r.json())\r\n                .then(firstData => {\r\n                    const firstItems = firstData.items || [];\r\n                    const pages = (typeof firstData.total_pages === 'number') ? firstData.total_pages : 1;\r\n                    if (pages <= 1) {\r\n                        rankAllItemsCache = firstItems;\r\n                        return rankAllItemsCache;\r\n                    }\r\n\r\n                    const requests = [];\r\n                    for (let p = 2; p <= pages; p++) {\r\n                        const url = API_BASE + '?' + new URLSearchParams({\r\n                            page: String(p),\r\n                            page_size: String(pageSize),\r\n                            _ts: String(ts)\r\n                        }).toString();\r\n                        requests.push(fetch(url, { cache: 'no-store' }).then(r => r.json()));\r\n                    }\r\n\r\n                    return Promise.all(requests).then(restPages => {\r\n                        const allItems = firstItems.slice();\r\n                        for (const pageData of restPages) {\r\n                            const pageItems = pageData.items || [];\r\n                            allItems.push(...pageItems);\r\n                        }\r\n                        rankAllItemsCache = allItems;\r\n                        return rankAllItemsCache;\r\n                    });\r\n                });\r\n        }\r\n\r\n        function fetchData() {\r\n            const requestId = ++lastRequestId;\r\n            const ts = Date.now();\r\n            if(currentSearch) {\r\n                const qs = new URLSearchParams({ name: currentSearch, _ts: String(ts) });\r\n                const url = API_BASE + '\/search?' + qs.toString();\r\n                fetch(url, { cache: 'no-store' })\r\n                    .then(r => r.json())\r\n                    .then(data => {\r\n                        if(requestId !== lastRequestId) return;\r\n                        const items = data.items || [];\r\n                        const baseItems = applyMinAgeFilter(items);\r\n                        const ageFilteredItems = applyAgeFilter(baseItems);\r\n                        const birthFilteredItems = applyBirthYearFilter(ageFilteredItems);\r\n                        const finalItems = applyCategoryFilter(birthFilteredItems);\r\n                        lastData = finalItems;\r\n                        totalItems = finalItems.length;\r\n                        totalPages = Math.ceil(totalItems \/ pageSize) || 1;\r\n                        if(currentPage > totalPages) currentPage = totalPages;\r\n                        const start = (currentPage-1)*pageSize;\r\n                        const end = start+pageSize;\r\n                        renderTable(finalItems.slice(start, end));\r\n                        renderPagination();\r\n                    })\r\n                    .catch(() => {\r\n                        if(requestId !== lastRequestId) return;\r\n                        renderTable([]);\r\n                        renderPagination();\r\n                    });\r\n            } else {\r\n                fetchAllRankItems(ts)\r\n                    .then(allItems => {\r\n                        if(requestId !== lastRequestId) return;\r\n                        if (!ageOptionsInitialized) {\r\n                            fillAgeOptions(allItems);\r\n                            ageOptionsInitialized = true;\r\n                        }\r\n                        const baseItems = applyMinAgeFilter(allItems);\r\n                        const ageFilteredItems = applyAgeFilter(baseItems);\r\n                        const birthFilteredItems = applyBirthYearFilter(ageFilteredItems);\r\n                        const finalItems = applyCategoryFilter(birthFilteredItems);\r\n                        lastData = finalItems;\r\n                        totalItems = finalItems.length;\r\n                        totalPages = Math.ceil(totalItems \/ pageSize) || 1;\r\n                        if(currentPage > totalPages) currentPage = totalPages;\r\n                        const start = (currentPage-1)*pageSize;\r\n                        const end = start+pageSize;\r\n                        renderTable(finalItems.slice(start, end));\r\n                        renderPagination();\r\n                    })\r\n                    .catch(() => {\r\n                        if(requestId !== lastRequestId) return;\r\n                        renderTable([]);\r\n                        renderPagination();\r\n                    });\r\n            }\r\n        }\r\n\r\n        document.getElementById('max-speed-search-btn').onclick = function() {\r\n            currentSearch = document.getElementById('max-speed-search-input').value.trim();\r\n            currentAge = document.getElementById('max-speed-age-select').value;\r\n            currentCategory = document.getElementById('max-speed-category-select').value;\r\n            currentBirthYearFrom = document.getElementById('max-speed-birth-year-from').value;\r\n            currentBirthYearTo = document.getElementById('max-speed-birth-year-to').value;\r\n            currentUnit = document.getElementById('max-speed-unit-select').value;\r\n            currentPage = 1;\r\n            fetchData();\r\n        };\r\n        document.getElementById('max-speed-search-input').addEventListener('keyup', function(e){\r\n            if(e.key === 'Enter') {\r\n                document.getElementById('max-speed-search-btn').click();\r\n            }\r\n        });\r\n\r\n        \/\/ Inicial\r\n        fillBirthYearOptions();\r\n        fetchData();\r\n    })();\r\n    <\/script>\r\n    <\/div>\n\t\t\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":{"footnotes":""},"class_list":["post-1432","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/origin.yoomedoo.com\/en\/wp-json\/wp\/v2\/pages\/1432","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/origin.yoomedoo.com\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/origin.yoomedoo.com\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/origin.yoomedoo.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/origin.yoomedoo.com\/en\/wp-json\/wp\/v2\/comments?post=1432"}],"version-history":[{"count":13,"href":"https:\/\/origin.yoomedoo.com\/en\/wp-json\/wp\/v2\/pages\/1432\/revisions"}],"predecessor-version":[{"id":1465,"href":"https:\/\/origin.yoomedoo.com\/en\/wp-json\/wp\/v2\/pages\/1432\/revisions\/1465"}],"wp:attachment":[{"href":"https:\/\/origin.yoomedoo.com\/en\/wp-json\/wp\/v2\/media?parent=1432"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}