Spaces:
Running
Running
Fix match detail UI: add lineups section, restore proper rendering
Browse files- static/match_detail.js +22 -43
static/match_detail.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
| 5 |
if(window._mdLoaded) return;
|
| 6 |
window._mdLoaded = true;
|
| 7 |
|
| 8 |
-
// Inject styles
|
| 9 |
if(!document.getElementById('md-css')){
|
| 10 |
const s=document.createElement('style');
|
| 11 |
s.id='md-css';
|
|
@@ -62,52 +61,39 @@
|
|
| 62 |
|
| 63 |
function esc(s){return String(s||'').replace(/[&<>"']/g,function(m){return{'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]})}
|
| 64 |
|
| 65 |
-
// === 1. Override openMatch to always set _currentEventId ===
|
| 66 |
const _origOpenMatch = window.openMatch;
|
| 67 |
window.openMatch = function(id){
|
| 68 |
if(id) window._currentEventId = String(id);
|
| 69 |
if(_origOpenMatch) _origOpenMatch.call(this, id);
|
| 70 |
-
// Switch to detail tab
|
| 71 |
setTimeout(function(){
|
| 72 |
injectDetailTab();
|
| 73 |
loadMatchTab('detail');
|
| 74 |
}, 300);
|
| 75 |
};
|
| 76 |
|
| 77 |
-
// === 2. Override loadMatchTab to handle 'detail' tab ===
|
| 78 |
const _origLoadMatchTab = window.loadMatchTab;
|
| 79 |
window.loadMatchTab = async function(tab){
|
| 80 |
if(tab !== 'detail'){
|
| 81 |
if(_origLoadMatchTab) _origLoadMatchTab.call(this, tab);
|
| 82 |
return;
|
| 83 |
}
|
| 84 |
-
|
| 85 |
-
// Update tab UI — highlight the Chi tiết tab
|
| 86 |
document.querySelectorAll('.mo-tab').forEach(function(t){ t.classList.remove('active'); });
|
| 87 |
document.querySelectorAll('.mo-tab').forEach(function(t){
|
| 88 |
const txt = t.textContent || '';
|
| 89 |
if(txt.includes('Chi tiết') || txt.includes('📋')) t.classList.add('active');
|
| 90 |
});
|
| 91 |
-
|
| 92 |
const el = document.getElementById('mo-body');
|
| 93 |
if(!el) return;
|
| 94 |
el.innerHTML = '<div class="md-no-data"><div style="display:inline-block;width:20px;height:20px;border:2px solid #333;border-top-color:#5cb87a;border-radius:50%;animation:mdspin .8s linear infinite;vertical-align:middle;margin-right:8px"></div>Đang tải chi tiết trận đấu...</div>';
|
| 95 |
-
|
| 96 |
const eventId = window._currentEventId;
|
| 97 |
if(!eventId){
|
| 98 |
el.innerHTML = '<div class="md-err">⚠️ Không tìm thấy mã trận đấu.<br><small>Hãy click vào một trận đấu cụ thể từ danh sách livescore.</small></div>';
|
| 99 |
return;
|
| 100 |
}
|
| 101 |
-
|
| 102 |
try{
|
| 103 |
const r = await fetch('/api/match/' + eventId + '/detail');
|
| 104 |
if(!r.ok){
|
| 105 |
-
/
|
| 106 |
-
if(r.status === 404){
|
| 107 |
-
el.innerHTML = '<div class="md-warn">⚠️ Không tìm thấy dữ liệu trận đấu.<br><small>Event ID: ' + esc(String(eventId)) + '</small><br><small style="color:#888">Trận đấu có thể chưa được cập nhật trên bongda.com.vn</small></div>';
|
| 108 |
-
} else {
|
| 109 |
-
el.innerHTML = '<div class="md-err">⚠️ Lỗi máy chủ (' + r.status + ').<br><small>Event ID: ' + esc(String(eventId)) + '</small><br><button onclick="window.loadMatchTab(\'detail\')" style="margin-top:8px;background:#0066cc;border:none;color:#fff;padding:6px 14px;border-radius:8px;font-size:11px;cursor:pointer">Thử lại</button></div>';
|
| 110 |
-
}
|
| 111 |
return;
|
| 112 |
}
|
| 113 |
const d = await r.json();
|
|
@@ -121,13 +107,12 @@
|
|
| 121 |
}
|
| 122 |
};
|
| 123 |
|
| 124 |
-
// === 3. Render match detail ===
|
| 125 |
function renderMatchDetail(el, data){
|
| 126 |
let h = '';
|
| 127 |
let hasAnyContent = false;
|
| 128 |
-
|
| 129 |
-
// 0. Pre-match info card (teams, league, date, stadium)
|
| 130 |
const info = data.info || {};
|
|
|
|
|
|
|
| 131 |
if(info.home_team && info.away_team){
|
| 132 |
hasAnyContent = true;
|
| 133 |
h += '<div class="md-pre-match">';
|
|
@@ -152,16 +137,12 @@
|
|
| 152 |
h += '</div>';
|
| 153 |
} else if(data.pre_match_html && data.pre_match_html.length > 10){
|
| 154 |
hasAnyContent = true;
|
| 155 |
-
//
|
| 156 |
-
let clean = data.pre_match_html
|
| 157 |
-
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
|
| 158 |
-
.replace(/<iframe[^>]*>[\s\S]*?<\/iframe>/gi, '')
|
| 159 |
-
.replace(/<ins[^>]*>[\s\S]*?<\/ins>/gi, '');
|
| 160 |
h += '<div class="md-sec"><div class="md-sec-title">📋 Thông tin trận đấu</div>';
|
| 161 |
h += '<div class="md-preview-text">' + clean + '</div></div>';
|
| 162 |
}
|
| 163 |
|
| 164 |
-
// 1. H2H Stats comparison
|
| 165 |
if(data.h2h_stats_parsed && Object.keys(data.h2h_stats_parsed).length > 0){
|
| 166 |
const parsed = data.h2h_stats_parsed;
|
| 167 |
const hasNonZero = Object.values(parsed).some(s => s.home !== "0" || s.away !== "0");
|
|
@@ -185,8 +166,7 @@
|
|
| 185 |
if(data.h2h && data.h2h.length > 0){
|
| 186 |
hasAnyContent = true;
|
| 187 |
h += '<div class="md-sec"><div class="md-sec-title">🔄 Lịch sử đối đầu</div>';
|
| 188 |
-
|
| 189 |
-
h2hList.forEach(function(m){
|
| 190 |
h += '<div class="md-h2h-item">';
|
| 191 |
h += '<span class="md-h2h-date">'+esc(m.date||'')+'</span>';
|
| 192 |
if(m.league) h += '<span class="md-h2h-league">'+esc(m.league)+'</span>';
|
|
@@ -197,58 +177,60 @@
|
|
| 197 |
h += '</span></div>';
|
| 198 |
});
|
| 199 |
h += '</div>';
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
}
|
| 201 |
|
| 202 |
// 3. Team form
|
| 203 |
if((data.home_form && data.home_form.length > 0) || (data.away_form && data.away_form.length > 0)){
|
| 204 |
hasAnyContent = true;
|
| 205 |
h += '<div class="md-sec"><div class="md-sec-title">📈 Phong độ gần đây</div>';
|
| 206 |
-
|
| 207 |
if(data.home_form && data.home_form.length > 0){
|
| 208 |
h += '<div class="md-form-section">';
|
| 209 |
h += '<div class="md-form-team-label">🏠 Đội nhà</div>';
|
| 210 |
-
data.home_form.slice(0, 5).forEach(function(m){
|
| 211 |
-
h += renderFormItem(m);
|
| 212 |
-
});
|
| 213 |
h += '</div>';
|
| 214 |
}
|
| 215 |
-
|
| 216 |
if(data.away_form && data.away_form.length > 0){
|
| 217 |
h += '<div class="md-form-section">';
|
| 218 |
h += '<div class="md-form-team-label">✈️ Đội khách</div>';
|
| 219 |
-
data.away_form.slice(0, 5).forEach(function(m){
|
| 220 |
-
h += renderFormItem(m);
|
| 221 |
-
});
|
| 222 |
h += '</div>';
|
| 223 |
}
|
| 224 |
h += '</div>';
|
| 225 |
}
|
| 226 |
|
| 227 |
-
// 4.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
if(data.stats_html && data.stats_html.length > 10){
|
| 229 |
hasAnyContent = true;
|
| 230 |
h += '<div class="md-sec"><div class="md-sec-title">🌟 Thông số cầu thủ</div>';
|
| 231 |
h += '<div>' + data.stats_html.replace(/<script[^>]*>[\s\S]*?<\/script>/gi,'').replace(/<iframe[^>]*>[\s\S]*?<\/iframe>/gi,'') + '</div></div>';
|
| 232 |
}
|
| 233 |
|
| 234 |
-
//
|
| 235 |
if(data.commentaries_html && data.commentaries_html.length > 10){
|
| 236 |
hasAnyContent = true;
|
| 237 |
h += '<div class="md-sec"><div class="md-sec-title">📢 Diễn biến</div>';
|
| 238 |
h += '<div class="md-preview-text">' + data.commentaries_html.replace(/<script[^>]*>[\s\S]*?<\/script>/gi,'').replace(/<iframe[^>]*>[\s\S]*?<\/iframe>/gi,'') + '</div></div>';
|
| 239 |
}
|
| 240 |
|
| 241 |
-
// If no content at all, show appropriate message
|
| 242 |
if(!h){
|
| 243 |
if(data.found === false){
|
| 244 |
-
h = '<div class="md-warn">⚠️ Không tìm thấy dữ liệu trận đấu.<br><small>Event ID: ' + esc(String(data.event_id||'')) + '</small><br><small style="color:#888;margin-top:4px;display:block">Nguyên nhân có thể:<br>• Mã trận đấu không đúng<br>• Trận đấu chưa được cập nhật
|
| 245 |
} else {
|
| 246 |
h = '<div class="md-no-data">⏳ Chưa có chi tiết cho trận đấu này.<br><small style="color:#666">Dữ liệu sẽ cập nhật khi giải khởi tranh hoặc trận đấu bắt đầu.</small></div>';
|
| 247 |
}
|
| 248 |
}
|
| 249 |
-
|
| 250 |
el.innerHTML = h;
|
| 251 |
-
// Prevent link navigation inside overlay
|
| 252 |
el.querySelectorAll('a').forEach(function(a){ a.addEventListener('click', function(e){ e.preventDefault(); }); });
|
| 253 |
}
|
| 254 |
|
|
@@ -264,7 +246,6 @@
|
|
| 264 |
return h;
|
| 265 |
}
|
| 266 |
|
| 267 |
-
// === 4. Inject "Chi tiết" tab into overlay ===
|
| 268 |
function injectDetailTab(){
|
| 269 |
const tabs = document.querySelector('.mo-tabs');
|
| 270 |
if(!tabs || document.querySelector('.mo-tab-detail')) return;
|
|
@@ -275,7 +256,6 @@
|
|
| 275 |
tabs.insertBefore(d, tabs.firstChild);
|
| 276 |
}
|
| 277 |
|
| 278 |
-
// === 5. Intercept clicks in livescore to capture event_id ===
|
| 279 |
document.addEventListener('click', function(e){
|
| 280 |
const md = e.target.closest('.match-detail');
|
| 281 |
if(!md) return;
|
|
@@ -287,7 +267,6 @@
|
|
| 287 |
}
|
| 288 |
}, true);
|
| 289 |
|
| 290 |
-
// Try to inject tab on DOM ready
|
| 291 |
if(document.readyState==='loading') document.addEventListener('DOMContentLoaded', function(){ setTimeout(injectDetailTab, 1000); });
|
| 292 |
else setTimeout(injectDetailTab, 1000);
|
| 293 |
})();
|
|
|
|
| 5 |
if(window._mdLoaded) return;
|
| 6 |
window._mdLoaded = true;
|
| 7 |
|
|
|
|
| 8 |
if(!document.getElementById('md-css')){
|
| 9 |
const s=document.createElement('style');
|
| 10 |
s.id='md-css';
|
|
|
|
| 61 |
|
| 62 |
function esc(s){return String(s||'').replace(/[&<>"']/g,function(m){return{'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]})}
|
| 63 |
|
|
|
|
| 64 |
const _origOpenMatch = window.openMatch;
|
| 65 |
window.openMatch = function(id){
|
| 66 |
if(id) window._currentEventId = String(id);
|
| 67 |
if(_origOpenMatch) _origOpenMatch.call(this, id);
|
|
|
|
| 68 |
setTimeout(function(){
|
| 69 |
injectDetailTab();
|
| 70 |
loadMatchTab('detail');
|
| 71 |
}, 300);
|
| 72 |
};
|
| 73 |
|
|
|
|
| 74 |
const _origLoadMatchTab = window.loadMatchTab;
|
| 75 |
window.loadMatchTab = async function(tab){
|
| 76 |
if(tab !== 'detail'){
|
| 77 |
if(_origLoadMatchTab) _origLoadMatchTab.call(this, tab);
|
| 78 |
return;
|
| 79 |
}
|
|
|
|
|
|
|
| 80 |
document.querySelectorAll('.mo-tab').forEach(function(t){ t.classList.remove('active'); });
|
| 81 |
document.querySelectorAll('.mo-tab').forEach(function(t){
|
| 82 |
const txt = t.textContent || '';
|
| 83 |
if(txt.includes('Chi tiết') || txt.includes('📋')) t.classList.add('active');
|
| 84 |
});
|
|
|
|
| 85 |
const el = document.getElementById('mo-body');
|
| 86 |
if(!el) return;
|
| 87 |
el.innerHTML = '<div class="md-no-data"><div style="display:inline-block;width:20px;height:20px;border:2px solid #333;border-top-color:#5cb87a;border-radius:50%;animation:mdspin .8s linear infinite;vertical-align:middle;margin-right:8px"></div>Đang tải chi tiết trận đấu...</div>';
|
|
|
|
| 88 |
const eventId = window._currentEventId;
|
| 89 |
if(!eventId){
|
| 90 |
el.innerHTML = '<div class="md-err">⚠️ Không tìm thấy mã trận đấu.<br><small>Hãy click vào một trận đấu cụ thể từ danh sách livescore.</small></div>';
|
| 91 |
return;
|
| 92 |
}
|
|
|
|
| 93 |
try{
|
| 94 |
const r = await fetch('/api/match/' + eventId + '/detail');
|
| 95 |
if(!r.ok){
|
| 96 |
+
el.innerHTML = '<div class="md-err">⚠️ Lỗi máy chủ (' + r.status + ').<br><small>Event ID: ' + esc(String(eventId)) + '</small><br><button onclick="window.loadMatchTab(\'detail\')" style="margin-top:8px;background:#0066cc;border:none;color:#fff;padding:6px 14px;border-radius:8px;font-size:11px;cursor:pointer">Thử lại</button></div>';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
return;
|
| 98 |
}
|
| 99 |
const d = await r.json();
|
|
|
|
| 107 |
}
|
| 108 |
};
|
| 109 |
|
|
|
|
| 110 |
function renderMatchDetail(el, data){
|
| 111 |
let h = '';
|
| 112 |
let hasAnyContent = false;
|
|
|
|
|
|
|
| 113 |
const info = data.info || {};
|
| 114 |
+
|
| 115 |
+
// 0. Pre-match info card
|
| 116 |
if(info.home_team && info.away_team){
|
| 117 |
hasAnyContent = true;
|
| 118 |
h += '<div class="md-pre-match">';
|
|
|
|
| 137 |
h += '</div>';
|
| 138 |
} else if(data.pre_match_html && data.pre_match_html.length > 10){
|
| 139 |
hasAnyContent = true;
|
| 140 |
+
let clean = data.pre_match_html.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '').replace(/<iframe[^>]*>[\s\S]*?<\/iframe>/gi, '').replace(/<ins[^>]*>[\s\S]*?<\/ins>/gi, '');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
h += '<div class="md-sec"><div class="md-sec-title">📋 Thông tin trận đấu</div>';
|
| 142 |
h += '<div class="md-preview-text">' + clean + '</div></div>';
|
| 143 |
}
|
| 144 |
|
| 145 |
+
// 1. H2H Stats comparison
|
| 146 |
if(data.h2h_stats_parsed && Object.keys(data.h2h_stats_parsed).length > 0){
|
| 147 |
const parsed = data.h2h_stats_parsed;
|
| 148 |
const hasNonZero = Object.values(parsed).some(s => s.home !== "0" || s.away !== "0");
|
|
|
|
| 166 |
if(data.h2h && data.h2h.length > 0){
|
| 167 |
hasAnyContent = true;
|
| 168 |
h += '<div class="md-sec"><div class="md-sec-title">🔄 Lịch sử đối đầu</div>';
|
| 169 |
+
data.h2h.slice(0, 10).forEach(function(m){
|
|
|
|
| 170 |
h += '<div class="md-h2h-item">';
|
| 171 |
h += '<span class="md-h2h-date">'+esc(m.date||'')+'</span>';
|
| 172 |
if(m.league) h += '<span class="md-h2h-league">'+esc(m.league)+'</span>';
|
|
|
|
| 177 |
h += '</span></div>';
|
| 178 |
});
|
| 179 |
h += '</div>';
|
| 180 |
+
} else if(data.h2h_html && data.h2h_html.length > 10){
|
| 181 |
+
hasAnyContent = true;
|
| 182 |
+
h += '<div class="md-sec"><div class="md-sec-title">🔄 Lịch sử đối đầu</div>';
|
| 183 |
+
h += '<div>' + data.h2h_html.replace(/<script[^>]*>[\s\S]*?<\/script>/gi,'').replace(/<iframe[^>]*>[\s\S]*?<\/iframe>/gi,'') + '</div></div>';
|
| 184 |
}
|
| 185 |
|
| 186 |
// 3. Team form
|
| 187 |
if((data.home_form && data.home_form.length > 0) || (data.away_form && data.away_form.length > 0)){
|
| 188 |
hasAnyContent = true;
|
| 189 |
h += '<div class="md-sec"><div class="md-sec-title">📈 Phong độ gần đây</div>';
|
|
|
|
| 190 |
if(data.home_form && data.home_form.length > 0){
|
| 191 |
h += '<div class="md-form-section">';
|
| 192 |
h += '<div class="md-form-team-label">🏠 Đội nhà</div>';
|
| 193 |
+
data.home_form.slice(0, 5).forEach(function(m){ h += renderFormItem(m); });
|
|
|
|
|
|
|
| 194 |
h += '</div>';
|
| 195 |
}
|
|
|
|
| 196 |
if(data.away_form && data.away_form.length > 0){
|
| 197 |
h += '<div class="md-form-section">';
|
| 198 |
h += '<div class="md-form-team-label">✈️ Đội khách</div>';
|
| 199 |
+
data.away_form.slice(0, 5).forEach(function(m){ h += renderFormItem(m); });
|
|
|
|
|
|
|
| 200 |
h += '</div>';
|
| 201 |
}
|
| 202 |
h += '</div>';
|
| 203 |
}
|
| 204 |
|
| 205 |
+
// 4. Lineups
|
| 206 |
+
if(data.lineups_html && data.lineups_html.length > 10){
|
| 207 |
+
hasAnyContent = true;
|
| 208 |
+
h += '<div class="md-sec"><div class="md-sec-title">📋 Đội hình</div>';
|
| 209 |
+
h += '<div>' + data.lineups_html.replace(/<script[^>]*>[\s\S]*?<\/script>/gi,'').replace(/<iframe[^>]*>[\s\S]*?<\/iframe>/gi,'') + '</div></div>';
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
// 5. Player performance stats
|
| 213 |
if(data.stats_html && data.stats_html.length > 10){
|
| 214 |
hasAnyContent = true;
|
| 215 |
h += '<div class="md-sec"><div class="md-sec-title">🌟 Thông số cầu thủ</div>';
|
| 216 |
h += '<div>' + data.stats_html.replace(/<script[^>]*>[\s\S]*?<\/script>/gi,'').replace(/<iframe[^>]*>[\s\S]*?<\/iframe>/gi,'') + '</div></div>';
|
| 217 |
}
|
| 218 |
|
| 219 |
+
// 6. Commentaries
|
| 220 |
if(data.commentaries_html && data.commentaries_html.length > 10){
|
| 221 |
hasAnyContent = true;
|
| 222 |
h += '<div class="md-sec"><div class="md-sec-title">📢 Diễn biến</div>';
|
| 223 |
h += '<div class="md-preview-text">' + data.commentaries_html.replace(/<script[^>]*>[\s\S]*?<\/script>/gi,'').replace(/<iframe[^>]*>[\s\S]*?<\/iframe>/gi,'') + '</div></div>';
|
| 224 |
}
|
| 225 |
|
|
|
|
| 226 |
if(!h){
|
| 227 |
if(data.found === false){
|
| 228 |
+
h = '<div class="md-warn">⚠️ Không tìm thấy dữ liệu trận đấu.<br><small>Event ID: ' + esc(String(data.event_id||'')) + '</small><br><small style="color:#888;margin-top:4px;display:block">Nguyên nhân có thể:<br>• Mã trận đấu không đúng<br>• Trận đấu chưa được cập nhật<br>• Dữ liệu chưa có sẵn</small></div>';
|
| 229 |
} else {
|
| 230 |
h = '<div class="md-no-data">⏳ Chưa có chi tiết cho trận đấu này.<br><small style="color:#666">Dữ liệu sẽ cập nhật khi giải khởi tranh hoặc trận đấu bắt đầu.</small></div>';
|
| 231 |
}
|
| 232 |
}
|
|
|
|
| 233 |
el.innerHTML = h;
|
|
|
|
| 234 |
el.querySelectorAll('a').forEach(function(a){ a.addEventListener('click', function(e){ e.preventDefault(); }); });
|
| 235 |
}
|
| 236 |
|
|
|
|
| 246 |
return h;
|
| 247 |
}
|
| 248 |
|
|
|
|
| 249 |
function injectDetailTab(){
|
| 250 |
const tabs = document.querySelector('.mo-tabs');
|
| 251 |
if(!tabs || document.querySelector('.mo-tab-detail')) return;
|
|
|
|
| 256 |
tabs.insertBefore(d, tabs.firstChild);
|
| 257 |
}
|
| 258 |
|
|
|
|
| 259 |
document.addEventListener('click', function(e){
|
| 260 |
const md = e.target.closest('.match-detail');
|
| 261 |
if(!md) return;
|
|
|
|
| 267 |
}
|
| 268 |
}, true);
|
| 269 |
|
|
|
|
| 270 |
if(document.readyState==='loading') document.addEventListener('DOMContentLoaded', function(){ setTimeout(injectDetailTab, 1000); });
|
| 271 |
else setTimeout(injectDetailTab, 1000);
|
| 272 |
})();
|