PageSourceSearch

https://nhandinhkeonhacai.net/wp-content/plugins/sports/ty-le-keo-standalone/script-sidebar-bxh.js?ver=2.4.0

js nhandinhkeonhacai.net collected 2026-10-09 21:52:55 UTC 11,078 bytes, 319 lines download raw bytes

1// Wrap in IIFE to avoid global namespace pollution
2(function() {
3'use strict';
4
5// ========== CONFIG ==========
6const API_BASE_URL = 'https://api.1gom.kim';
7const TOP_LEAGUE_CODES = ['ENG1', 'ITA1', 'GER1', 'SPA1', 'FRA1'];
8
9// ========== STATE ==========
10const state = {
11    leagues: [], // { id, name, code, flag, seasonYear, standings }
12    selectedLeagueId: null,
13    isLoading: false
14};
15
16// ========== DOM ELEMENTS ==========
17const elements = {
18    loading: document.getElementById('sidebarLoading'),
19    leagueSelect: document.getElementById('leagueSelect'),
20    standingsTable: document.getElementById('standingsTable'),
21    container: document.getElementById('sidebarBxhContainer')
22};
23
24// ========== UTILITIES ==========
25function getLeagueName(code) {
26    const names = {
27        'ENG1': 'Ngoại hạng Anh',
28        'ITA1': 'Serie A',
29        'GER1': 'Bundesliga',
30        'SPA1': 'La Liga',
31        'FRA1': 'Ligue 1'
32    };
33    return names[code] || code;
34}
35
36function getRankColor(position) {
37    if (position === 1) return '#5793fc';
38    if (position === 2) return '#5793fc';
39    if (position === 3) return '#5793fc';
40    return '#94a3b8';
41}
42
43function getRowBackground(position) {
44    if (position === 1) return '#f0f9ff';
45    if (position % 2 === 0) return '#f8fafc';
46    return '#ffffff';
47}
48
49// ========== API CALLS ==========
50async function fetchCompetitions() {
51    try {
52        // Build query params like CustomCardRank.tsx
53        const params = new URLSearchParams({
54            limit: 5,
55            page: 1,
56            'populate[country]': '*',
57            'condition[tier][$lte]': 10,
58            'sort[tier]': 'asc',
59            'sort[seq]': 'asc',
60            'sort[updatedAt]': 'desc'
61        });
62
63        const response = await fetch(`${API_BASE_URL}/competitions?${params}`);
64        const data = await response.json();
65
66        console.log('📦 Competitions response:', data);
67
68        if (data.success && data.data?.items) {
69            // Filter chỉ lấy top 5 leagues theo code
70            const topLeagues = data.data.items.filter(comp =>
71                TOP_LEAGUE_CODES.includes(comp.code)
72            );
73
74            return topLeagues.map(comp => ({
75                id: comp._id,
76                name: getLeagueName(comp.code),
77                code: comp.code,
78                flag: comp.country?.flag || '',
79                seasonYear: comp.seasons?.[0]?.year || new Date().getFullYear()
80            }));
81        }
82        return [];
83    } catch (error) {
84        console.error('Error fetching competitions:', error);
85        return [];
86    }
87}
88
89async function fetchStandings(competitionId, seasonYear) {
90    try {
91        const params = new URLSearchParams({
92            competitionId,
93            seasonYear,
94            type: 'all'
95        });
96
97        const response = await fetch(`${API_BASE_URL}/standings/overall?${params}`);
98        const data = await response.json();
99
100        console.log('📊 Standings response:', { competitionId, data });
101
102        if (data.success && data.data) {
103            if (Array.isArray(data.data)) {
104                return data.data;
105            } else if (data.data.items && Array.isArray(data.data.items)) {
106                return data.data.items;
107            }
108        }
109        return [];
110    } catch (error) {
111        console.error('Error fetching standings:', error);
112        return [];
113    }
114}
115
116// ========== RENDERING ==========
117function showLoading() {
118    if (elements.loading) elements.loading.style.display = 'flex';
119    if (elements.standingsTable) elements.standingsTable.style.display = 'none';
120}
121
122function hideLoading() {
123    if (elements.loading) elements.loading.style.display = 'none';
124    if (elements.standingsTable) elements.standingsTable.style.display = 'block';
125}
126
127function renderLeagueSelect() {
128    if (!elements.leagueSelect || state.leagues.length === 0) return;
129
130    const selectedLeague = state.leagues.find(l => l.id === state.selectedLeagueId) || state.leagues[0];
131
132    elements.leagueSelect.innerHTML = `
133        <button class="sidebar-league-select-btn" id="leagueSelectBtn">
134            <div class="sidebar-league-select-content">
135                ${selectedLeague.flag ? `
136                    <img src="${selectedLeague.flag}"
137                         alt="${selectedLeague.name}"
138                         class="sidebar-league-flag">
139                ` : ''}
140                <span class="sidebar-league-name">${selectedLeague.name}</span>
141            </div>
142            <svg class="sidebar-select-arrow" width="12" height="12" viewBox="0 0 12 12" fill="none">
143                <path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
144            </svg>
145        </button>
146        <div class="sidebar-league-dropdown" id="leagueDropdown" style="display: none;">
147            ${state.leagues.map(league => `
148                <div class="sidebar-league-option ${league.id === state.selectedLeagueId ? 'active' : ''}"
149                     data-league-id="${league.id}">
150                    ${league.flag ? `
151                        <img src="${league.flag}"
152                             alt="${league.name}"
153                             class="sidebar-league-flag">
154                    ` : ''}
155                    <span>${league.name}</span>
156                </div>
157            `).join('')}
158        </div>
159    `;
160
161    // Add click handlers
162    const btn = document.getElementById('leagueSelectBtn');
163    const dropdown = document.getElementById('leagueDropdown');
164
165    if (btn && dropdown) {
166        btn.addEventListener('click', (e) => {
167            e.stopPropagation();
168            dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none';
169        });
170
171        document.addEventListener('click', () => {
172            dropdown.style.display = 'none';
173        });
174
175        dropdown.querySelectorAll('.sidebar-league-option').forEach(option => {
176            option.addEventListener('click', (e) => {
177                e.stopPropagation();
178                const leagueId = option.getAttribute('data-league-id');
179                state.selectedLeagueId = leagueId;
180                dropdown.style.display = 'none';
181                renderLeagueSelect();
182                renderStandings();
183            });
184        });
185    }
186}
187
188function renderStandings() {
189    if (!elements.standingsTable) return;
190
191    const selectedLeague = state.leagues.find(l => l.id === state.selectedLeagueId);
192    if (!selectedLeague || !selectedLeague.standings) {
193        elements.standingsTable.innerHTML = '<p class="sidebar-empty">Không có dữ liệu</p>';
194        return;
195    }
196
197    const standings = selectedLeague.standings;
198
199    if (!Array.isArray(standings) || standings.length === 0) {
200        elements.standingsTable.innerHTML = '<p class="sidebar-empty">Không có dữ liệu bảng xếp hạng</p>';
201        return;
202    }
203
204    // Debug first team
205    if (standings.length > 0) {
206        console.log('👥 First team structure:', standings[0]);
207    }
208
209    elements.standingsTable.innerHTML = `
210        <!-- Table Header -->
211        <div class="sidebar-table-header">
212            <div class="sidebar-col-rank">#</div>
213            <div class="sidebar-col-team">Đội</div>
214            <div class="sidebar-col-stat">Trận</div>
215            <div class="sidebar-col-stat">HS</div>
216            <div class="sidebar-col-points">Điểm</div>
217        </div>
218
219        <!-- Table Body -->
220        ${standings.map((team, index) => {
221            const position = index + 1;
222            const bgColor = getRowBackground(position);
223            const rankColor = getRankColor(position);
224
225            return `
226                <div class="sidebar-table-row" style="background: ${bgColor};">
227                    <div class="sidebar-col-rank">
228                        <span class="sidebar-rank-badge" style="background: ${rankColor};">
229                            ${position}
230                        </span>
231                    </div>
232                    <div class="sidebar-col-team">
233                        ${team.team?.image ? `
234                            <img src="${team.team.image}"
235                                 alt="${team.team.name || team.team.nameAlt || ''}"
236                                 class="sidebar-team-logo"
237                                 onerror="this.style.display='none'">
238                        ` : ''}
239                        <span class="sidebar-team-name">${team.team?.name || team.team?.nameAlt || team.nameAlt || team.name || 'Unknown'}</span>
240                    </div>
241                    <div class="sidebar-col-stat">${team.all?.played || team.played || 0}</div>
242                    <div class="sidebar-col-stat">${(team.all?.goalsDiff !== undefined ? team.all.goalsDiff : (team.goalsDiff || 0)) >= 0 ? '+' : ''}${team.all?.goalsDiff !== undefined ? team.all.goalsDiff : (team.goalsDiff || 0)}</div>
243                    <div class="sidebar-col-points">
244                        <span class="sidebar-points-badge" style="background: ${rankColor};">
245                            ${team.all?.points || team.points || 0}
246                        </span>
247                    </div>
248                </div>
249            `;
250        }).join('')}
251    `;
252}
253
254// ========== INITIALIZATION ==========
255async function loadData() {
256    if (state.isLoading) return;
257
258    try {
259        state.isLoading = true;
260        showLoading();
261
262        // 1. Fetch competitions (như CustomCardRank.tsx)
263        const leagues = await fetchCompetitions();
264
265        if (leagues.length === 0) {
266            console.error('No leagues found');
267            elements.standingsTable.innerHTML = '<p class="sidebar-empty">Không tìm thấy giải đấu</p>';
268            hideLoading();
269            return;
270        }
271
272        console.log('✅ Fetched leagues:', leagues);
273
274        // 2. Fetch standings for all leagues in parallel
275        const standingsPromises = leagues.map(league =>
276            fetchStandings(league.id, league.seasonYear)
277        );
278        const standingsResults = await Promise.all(standingsPromises);
279
280        // 3. Map standings to leagues
281        state.leagues = leagues.map((league, index) => ({
282            ...league,
283            standings: standingsResults[index]
284        }));
285
286        // 4. Select first league by default
287        state.selectedLeagueId = state.leagues[0].id;
288
289        // 5. Render UI
290        renderLeagueSelect();
291        renderStandings();
292
293    } catch (error) {
294        console.error('Error loading sidebar standings:', error);
295        elements.standingsTable.innerHTML = '<p class="sidebar-empty">Có lỗi xảy ra khi tải dữ liệu</p>';
296    } finally {
297        state.isLoading = false;
298        hideLoading();
299    }
300}
301
302// ========== START ==========
303function init() {
304    if (!elements.container) {
305        console.error('Sidebar BXH container not found');
306        return;
307    }
308
309    loadData();
310}
311
312// Auto-initialize when DOM is ready
313if (document.readyState === 'loading') {
314    document.addEventListener('DOMContentLoaded', init);
315} else {
316    init();
317}
318
319})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.