Spaces:
Running
Running
Rewrite match_detail.js: structured timeline, events, stats, score+status display
Browse files- static/match_detail.js +141 -66
static/match_detail.js
CHANGED
|
@@ -13,9 +13,15 @@
|
|
| 13 |
.md-pre-league{font-size:9px;color:#5cb87a;font-weight:700;text-transform:uppercase;letter-spacing:1px;margin-bottom:6px}
|
| 14 |
.md-pre-teams{display:flex;align-items:center;justify-content:center;gap:12px}
|
| 15 |
.md-pre-team{display:flex;flex-direction:column;align-items:center;gap:4px;flex:1}
|
| 16 |
-
.md-pre-team img{width:
|
| 17 |
-
.md-pre-team span{font-size:
|
| 18 |
-
.md-pre-vs{font-size:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
.md-pre-info{display:flex;gap:12px;justify-content:center;margin-top:10px;flex-wrap:wrap}
|
| 20 |
.md-pre-info-item{font-size:10px;color:#aaa;display:flex;align-items:center;gap:3px}
|
| 21 |
.md-pre-info-item b{color:#ddd;font-size:11px}
|
|
@@ -35,56 +41,62 @@
|
|
| 35 |
.md-form-item{display:flex;align-items:center;gap:6px;padding:5px 6px;background:#1a1a1a;border-radius:6px;margin-bottom:2px;font-size:10px}
|
| 36 |
.md-form-result{padding:1px 5px;border-radius:3px;font-size:8px;font-weight:800;color:#fff;flex-shrink:0}
|
| 37 |
.md-form-result.T{background:#2d8659} .md-form-result.B{background:#c0392b} .md-form-result.H{background:#666}
|
| 38 |
-
.md-form-date{font-size:9px;color:#888;min-width:
|
| 39 |
.md-form-teams{flex:1;display:flex;align-items:center;gap:4px;min-width:0}
|
| 40 |
.md-form-home{text-align:right;flex:1;color:#ddd;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
| 41 |
-
.md-form-away{text-align:left;flex:1;color:#ddd;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
| 42 |
.md-form-score{font-size:10px;font-weight:700;color:#f0c040;min-width:30px;text-align:center;flex-shrink:0}
|
|
|
|
| 43 |
.md-form-section{margin-bottom:10px}
|
| 44 |
-
.md-form-team-label{font-size:
|
| 45 |
-
.md-
|
| 46 |
-
.md-
|
| 47 |
-
.md-
|
| 48 |
-
.
|
| 49 |
-
.md-raw-html{
|
| 50 |
-
.md-raw-html
|
| 51 |
-
.md-raw-html
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
`;
|
| 53 |
document.head.appendChild(s);
|
| 54 |
}
|
| 55 |
|
| 56 |
-
function esc(s){
|
| 57 |
|
| 58 |
function sanitizeHtml(html){
|
| 59 |
if(!html) return '';
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
}
|
| 68 |
|
| 69 |
-
const _origOpenMatch = window.openMatch;
|
| 70 |
-
window.openMatch = function(id, url){
|
| 71 |
-
if(id) window._currentEventId = String(id);
|
| 72 |
-
if(url) window._currentMatchUrl = String(url);
|
| 73 |
-
if(_origOpenMatch) _origOpenMatch.call(this, id);
|
| 74 |
-
setTimeout(function(){
|
| 75 |
-
injectDetailTab();
|
| 76 |
-
loadMatchTab('detail');
|
| 77 |
-
}, 300);
|
| 78 |
-
};
|
| 79 |
-
|
| 80 |
-
const _origLoadMatchTab = window.loadMatchTab;
|
| 81 |
window.loadMatchTab = async function(tab){
|
| 82 |
if(tab !== 'detail'){
|
| 83 |
if(_origLoadMatchTab) _origLoadMatchTab.call(this, tab);
|
| 84 |
return;
|
| 85 |
}
|
| 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 |
});
|
|
@@ -123,36 +135,67 @@
|
|
| 123 |
let hasAnyContent = false;
|
| 124 |
const info = data.info || {};
|
| 125 |
|
| 126 |
-
// 0.
|
| 127 |
if(info.home_team && info.away_team){
|
| 128 |
hasAnyContent = true;
|
| 129 |
h += '<div class="md-pre-match">';
|
| 130 |
if(info.league) h += '<div class="md-pre-league">'+esc(info.league)+'</div>';
|
| 131 |
h += '<div class="md-pre-teams">';
|
| 132 |
h += '<div class="md-pre-team">';
|
| 133 |
-
if(info.home_logo) h += '<img src="'+esc(info.home_logo)+'" alt="">';
|
| 134 |
h += '<span>'+esc(info.home_team)+'</span></div>';
|
| 135 |
-
h += '<div class="md-pre-vs">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
h += '<div class="md-pre-team">';
|
| 137 |
-
if(info.away_logo) h += '<img src="'+esc(info.away_logo)+'" alt="">';
|
| 138 |
h += '<span>'+esc(info.away_team)+'</span></div>';
|
| 139 |
h += '</div>';
|
| 140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
h += '<div class="md-pre-info">';
|
| 142 |
-
|
| 143 |
-
if(info.time) h += '<span class="md-pre-info-item">🕐 <b>'+esc(info.time)+'</b></span>';
|
| 144 |
-
if(info.stadium) h += '<span class="md-pre-info-item">🏟 <b>'+esc(info.stadium)+'</b></span>';
|
| 145 |
-
if(info.referee) h += '<span class="md-pre-info-item">👨⚖️ <b>'+esc(info.referee)+'</b></span>';
|
| 146 |
h += '</div>';
|
| 147 |
}
|
| 148 |
h += '</div>';
|
| 149 |
-
} else if(data.pre_match_html && data.pre_match_html.length > 10){
|
| 150 |
-
hasAnyContent = true;
|
| 151 |
-
h += '<div class="md-sec"><div class="md-sec-title">📋 Thông tin trận đấu</div>';
|
| 152 |
-
h += '<div class="md-preview-text md-raw-html">' + sanitizeHtml(data.pre_match_html) + '</div></div>';
|
| 153 |
}
|
| 154 |
|
| 155 |
-
// 1.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
if(data.h2h_stats_parsed && Object.keys(data.h2h_stats_parsed).length > 0){
|
| 157 |
const parsed = data.h2h_stats_parsed;
|
| 158 |
const hasNonZero = Object.values(parsed).some(s => s.home !== "0" || s.away !== "0");
|
|
@@ -170,7 +213,7 @@
|
|
| 170 |
}
|
| 171 |
}
|
| 172 |
|
| 173 |
-
//
|
| 174 |
if(data.h2h && data.h2h.length > 0){
|
| 175 |
hasAnyContent = true;
|
| 176 |
h += '<div class="md-sec"><div class="md-sec-title">🔄 Lịch sử đối đầu</div>';
|
|
@@ -183,13 +226,9 @@
|
|
| 183 |
h += '<span class="md-h2h-away">'+esc(m.away||'')+'</span></span></div>';
|
| 184 |
});
|
| 185 |
h += '</div>';
|
| 186 |
-
} else if(data.h2h_html && data.h2h_html.length > 10){
|
| 187 |
-
hasAnyContent = true;
|
| 188 |
-
h += '<div class="md-sec"><div class="md-sec-title">🔄 Lịch sử đối đầu</div>';
|
| 189 |
-
h += '<div class="md-raw-html">' + sanitizeHtml(data.h2h_html) + '</div></div>';
|
| 190 |
}
|
| 191 |
|
| 192 |
-
//
|
| 193 |
if((data.home_form && data.home_form.length > 0) || (data.away_form && data.away_form.length > 0)){
|
| 194 |
hasAnyContent = true;
|
| 195 |
h += '<div class="md-sec"><div class="md-sec-title">📈 Phong độ gần đây</div>';
|
|
@@ -206,30 +245,65 @@
|
|
| 206 |
h += '</div>';
|
| 207 |
}
|
| 208 |
|
| 209 |
-
//
|
| 210 |
-
if(data.
|
| 211 |
hasAnyContent = true;
|
| 212 |
-
h += '<div class="md-sec"><div class="md-sec-title">
|
| 213 |
-
h += '<div class="md-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
}
|
| 215 |
|
| 216 |
-
//
|
| 217 |
-
if(data.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
hasAnyContent = true;
|
| 219 |
h += '<div class="md-sec"><div class="md-sec-title">🌟 Thông số cầu thủ</div>';
|
| 220 |
h += '<div class="md-raw-html">' + sanitizeHtml(data.stats_html) + '</div></div>';
|
| 221 |
}
|
| 222 |
|
| 223 |
-
|
| 224 |
-
if(data.commentaries_html && data.commentaries_html.length > 10){
|
| 225 |
hasAnyContent = true;
|
| 226 |
h += '<div class="md-sec"><div class="md-sec-title">📢 Diễn biến</div>';
|
| 227 |
h += '<div class="md-preview-text md-raw-html">' + sanitizeHtml(data.commentaries_html) + '</div></div>';
|
| 228 |
}
|
| 229 |
|
| 230 |
-
if(!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
if(data.found === false){
|
| 232 |
-
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
|
| 233 |
} else {
|
| 234 |
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>';
|
| 235 |
}
|
|
@@ -258,11 +332,12 @@
|
|
| 258 |
tabs.insertBefore(d, tabs.firstChild);
|
| 259 |
}
|
| 260 |
|
| 261 |
-
// Capture
|
| 262 |
document.addEventListener('click', function(e){
|
| 263 |
const md = e.target.closest('.match-detail');
|
| 264 |
if(!md) return;
|
| 265 |
-
|
|
|
|
| 266 |
if(link){
|
| 267 |
const href = link.getAttribute('href') || '';
|
| 268 |
const m = href.match(/\/tran-dau\/(\d+)\//);
|
|
|
|
| 13 |
.md-pre-league{font-size:9px;color:#5cb87a;font-weight:700;text-transform:uppercase;letter-spacing:1px;margin-bottom:6px}
|
| 14 |
.md-pre-teams{display:flex;align-items:center;justify-content:center;gap:12px}
|
| 15 |
.md-pre-team{display:flex;flex-direction:column;align-items:center;gap:4px;flex:1}
|
| 16 |
+
.md-pre-team img{width:36px;height:36px;object-fit:contain;background:#1a1a1a;border-radius:50%;padding:2px}
|
| 17 |
+
.md-pre-team span{font-size:12px;color:#ddd;text-align:center;font-weight:600}
|
| 18 |
+
.md-pre-vs{font-size:20px;font-weight:900;color:#f0c040;min-width:50px;text-align:center;display:flex;flex-direction:column;align-items:center;gap:2px}
|
| 19 |
+
.md-pre-vs .md-vs-label{font-size:9px;color:#888;font-weight:400}
|
| 20 |
+
.md-pre-vs .md-score{font-size:20px;font-weight:900;color:#f0c040}
|
| 21 |
+
.md-pre-vs .md-status{font-size:8px;font-weight:700;padding:1px 6px;border-radius:3px;letter-spacing:.5px}
|
| 22 |
+
.md-pre-vs .md-status.finished{background:#2d8659;color:#a8ffb8}
|
| 23 |
+
.md-pre-vs .md-status.live{background:#c0392b;color:#fff;animation:mdpulse 1.5s ease-in-out infinite}
|
| 24 |
+
.md-pre-vs .md-status.halftime{background:#b7950b;color:#fff}
|
| 25 |
.md-pre-info{display:flex;gap:12px;justify-content:center;margin-top:10px;flex-wrap:wrap}
|
| 26 |
.md-pre-info-item{font-size:10px;color:#aaa;display:flex;align-items:center;gap:3px}
|
| 27 |
.md-pre-info-item b{color:#ddd;font-size:11px}
|
|
|
|
| 41 |
.md-form-item{display:flex;align-items:center;gap:6px;padding:5px 6px;background:#1a1a1a;border-radius:6px;margin-bottom:2px;font-size:10px}
|
| 42 |
.md-form-result{padding:1px 5px;border-radius:3px;font-size:8px;font-weight:800;color:#fff;flex-shrink:0}
|
| 43 |
.md-form-result.T{background:#2d8659} .md-form-result.B{background:#c0392b} .md-form-result.H{background:#666}
|
| 44 |
+
.md-form-date{font-size:9px;color:#888;min-width:45px;flex-shrink:0}
|
| 45 |
.md-form-teams{flex:1;display:flex;align-items:center;gap:4px;min-width:0}
|
| 46 |
.md-form-home{text-align:right;flex:1;color:#ddd;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
|
|
| 47 |
.md-form-score{font-size:10px;font-weight:700;color:#f0c040;min-width:30px;text-align:center;flex-shrink:0}
|
| 48 |
+
.md-form-away{text-align:left;flex:1;color:#ddd;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
| 49 |
.md-form-section{margin-bottom:10px}
|
| 50 |
+
.md-form-team-label{font-size:10px;color:#888;margin-bottom:4px;font-weight:600}
|
| 51 |
+
.md-raw-html{overflow:hidden}
|
| 52 |
+
.md-raw-html img{max-width:100%;height:auto;display:block;margin:4px auto}
|
| 53 |
+
.md-raw-html table{width:100%;border-collapse:collapse;margin:6px 0;font-size:10px}
|
| 54 |
+
.md-raw-html table th,.md-raw-html table td{padding:4px 6px;border:1px solid #2a2a2a;text-align:center}
|
| 55 |
+
.md-raw-html table th{background:#1a2a1f;color:#5cb87a;font-weight:700}
|
| 56 |
+
.md-raw-html p{margin:4px 0}
|
| 57 |
+
.md-raw-html br{display:block;content:"";margin:2px 0}
|
| 58 |
+
.md-timeline{position:relative;padding-left:20px}
|
| 59 |
+
.md-timeline::before{content:"";position:absolute;left:7px;top:0;bottom:0;width:2px;background:#2a2a2a}
|
| 60 |
+
.md-tl-item{position:relative;padding:4px 0 4px 8px;font-size:11px;color:#ccc;border-bottom:1px solid #1a1a1a}
|
| 61 |
+
.md-tl-item::before{content:"";position:absolute;left:-17px;top:8px;width:6px;height:6px;border-radius:50%;background:#333;border:2px solid #555}
|
| 62 |
+
.md-tl-item.goal::before{background:#f0c040;border-color:#f0c040;box-shadow:0 0 6px rgba(240,192,64,.5)}
|
| 63 |
+
.md-tl-item.card::before{background:#e85d04;border-color:#e85d04}
|
| 64 |
+
.md-tl-item.sub::before{background:#2d8659;border-color:#2d8659}
|
| 65 |
+
.md-tl-time{font-size:9px;color:#888;font-weight:700;margin-right:6px;min-width:35px;display:inline-block}
|
| 66 |
+
.md-tl-half{font-size:9px;font-weight:800;color:#5cb87a;background:#1a2a1f;padding:1px 5px;border-radius:3px;margin:6px 0 2px 0;display:inline-block}
|
| 67 |
+
.md-events{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:4px}
|
| 68 |
+
.md-event-chip{font-size:9px;padding:2px 6px;border-radius:4px;background:#1a1a1a;color:#aaa;display:flex;align-items:center;gap:3px}
|
| 69 |
+
.md-event-chip.home{border-left:2px solid #5cb87a}
|
| 70 |
+
.md-event-chip.away{border-left:2px solid #e85d04}
|
| 71 |
+
.md-no-data{text-align:center;color:#666;padding:16px 8px;font-size:12px}
|
| 72 |
+
.md-err{color:#e85d04;font-size:12px;padding:8px;background:#2a1a1a;border-radius:6px;margin-bottom:6px}
|
| 73 |
+
.md-warn{color:#f0c040;font-size:12px;padding:8px;background:#2a2a1a;border-radius:6px;margin-bottom:6px}
|
| 74 |
+
@keyframes mdspin{to{transform:rotate(360deg)}}
|
| 75 |
+
@keyframes mdpulse{0%,100%{opacity:1}50%{opacity:.5}}
|
| 76 |
`;
|
| 77 |
document.head.appendChild(s);
|
| 78 |
}
|
| 79 |
|
| 80 |
+
function esc(s){ const d=document.createElement('div'); d.textContent=s||''; return d.innerHTML; }
|
| 81 |
|
| 82 |
function sanitizeHtml(html){
|
| 83 |
if(!html) return '';
|
| 84 |
+
let h = html.replace(/style="[^"]*"/gi, '');
|
| 85 |
+
h = h.replace(/<script[^>]*>.*?<\/script>/gi, '');
|
| 86 |
+
h = h.replace(/<iframe[^>]*>.*?<\/iframe>/gi, '');
|
| 87 |
+
h = h.replace(/<style[^>]*>.*?<\/style>/gi, '');
|
| 88 |
+
h = h.replace(/on\w+="[^"]*"/gi, '');
|
| 89 |
+
h = h.replace(/onclick="[^"]*"/gi, '');
|
| 90 |
+
return h;
|
| 91 |
}
|
| 92 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
window.loadMatchTab = async function(tab){
|
| 94 |
if(tab !== 'detail'){
|
| 95 |
if(_origLoadMatchTab) _origLoadMatchTab.call(this, tab);
|
| 96 |
return;
|
| 97 |
}
|
| 98 |
document.querySelectorAll('.mo-tab').forEach(function(t){ t.classList.remove('active'); });
|
| 99 |
+
document.querySelectorAll('. mo-tab').forEach(function(t){
|
| 100 |
const txt = t.textContent || '';
|
| 101 |
if(txt.includes('Chi tiết') || txt.includes('📋')) t.classList.add('active');
|
| 102 |
});
|
|
|
|
| 135 |
let hasAnyContent = false;
|
| 136 |
const info = data.info || {};
|
| 137 |
|
| 138 |
+
// 0. Match header card (teams + score + status)
|
| 139 |
if(info.home_team && info.away_team){
|
| 140 |
hasAnyContent = true;
|
| 141 |
h += '<div class="md-pre-match">';
|
| 142 |
if(info.league) h += '<div class="md-pre-league">'+esc(info.league)+'</div>';
|
| 143 |
h += '<div class="md-pre-teams">';
|
| 144 |
h += '<div class="md-pre-team">';
|
| 145 |
+
if(info.home_logo) h += '<img src="'+esc(info.home_logo)+'" alt="'+esc(info.home_team)+'">';
|
| 146 |
h += '<span>'+esc(info.home_team)+'</span></div>';
|
| 147 |
+
h += '<div class="md-pre-vs">';
|
| 148 |
+
if(info.score){
|
| 149 |
+
h += '<span class="md-score">'+esc(info.score)+'</span>';
|
| 150 |
+
} else {
|
| 151 |
+
h += '<span class="md-vs-label">VS</span>';
|
| 152 |
+
}
|
| 153 |
+
if(info.status_label){
|
| 154 |
+
const sl = info.status_label;
|
| 155 |
+
let cls = 'finished';
|
| 156 |
+
if(sl === 'LIVE' || sl === 'H1' || sl === 'H2') cls = 'live';
|
| 157 |
+
if(sl === 'HT' || sl === 'Nghỉ') cls = 'halftime';
|
| 158 |
+
h += '<span class="md-status '+cls+'">'+esc(sl)+'</span>';
|
| 159 |
+
}
|
| 160 |
+
h += '</div>';
|
| 161 |
h += '<div class="md-pre-team">';
|
| 162 |
+
if(info.away_logo) h += '<img src="'+esc(info.away_logo)+'" alt="'+esc(info.away_team)+'">';
|
| 163 |
h += '<span>'+esc(info.away_team)+'</span></div>';
|
| 164 |
h += '</div>';
|
| 165 |
+
// Info items
|
| 166 |
+
const infoItems = [];
|
| 167 |
+
if(info.datetime) infoItems.push('📅 <b>'+esc(info.datetime)+'</b>');
|
| 168 |
+
if(info.date && !info.datetime) infoItems.push('📅 <b>'+esc(info.date)+'</b>');
|
| 169 |
+
if(info.time) infoItems.push('🕐 <b>'+esc(info.time)+'</b>');
|
| 170 |
+
if(info.stadium) infoItems.push('🏟 <b>'+esc(info.stadium)+'</b>');
|
| 171 |
+
if(info.referee) infoItems.push('👨⚖️ <b>'+esc(info.referee)+'</b>');
|
| 172 |
+
if(infoItems.length > 0){
|
| 173 |
h += '<div class="md-pre-info">';
|
| 174 |
+
infoItems.forEach(function(item){ h += '<span class="md-pre-info-item">'+item+'</span>'; });
|
|
|
|
|
|
|
|
|
|
| 175 |
h += '</div>';
|
| 176 |
}
|
| 177 |
h += '</div>';
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
}
|
| 179 |
|
| 180 |
+
// 1. Match stats (from structured data)
|
| 181 |
+
if(data.stats_parsed && Object.keys(data.stats_parsed).length > 0){
|
| 182 |
+
const parsed = data.stats_parsed;
|
| 183 |
+
const hasNonZero = Object.values(parsed).some(s => s.home !== "0" || s.away !== "0");
|
| 184 |
+
if(hasNonZero){
|
| 185 |
+
hasAnyContent = true;
|
| 186 |
+
h += '<div class="md-sec"><div class="md-sec-title">📊 Thống kê trận đấu</div><div class="md-h2h-stats">';
|
| 187 |
+
for(const label in parsed){
|
| 188 |
+
const s = parsed[label];
|
| 189 |
+
h += '<div class="md-h2h-stat-row">';
|
| 190 |
+
h += '<span class="md-h2h-stat-home">'+esc(s.home)+'</span>';
|
| 191 |
+
h += '<span class="md-h2h-stat-label">'+esc(label)+'</span>';
|
| 192 |
+
h += '<span class="md-h2h-stat-away">'+esc(s.away)+'</span></div>';
|
| 193 |
+
}
|
| 194 |
+
h += '</div></div>';
|
| 195 |
+
}
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
// 2. H2H Stats (đối đầu)
|
| 199 |
if(data.h2h_stats_parsed && Object.keys(data.h2h_stats_parsed).length > 0){
|
| 200 |
const parsed = data.h2h_stats_parsed;
|
| 201 |
const hasNonZero = Object.values(parsed).some(s => s.home !== "0" || s.away !== "0");
|
|
|
|
| 213 |
}
|
| 214 |
}
|
| 215 |
|
| 216 |
+
// 3. H2H matches
|
| 217 |
if(data.h2h && data.h2h.length > 0){
|
| 218 |
hasAnyContent = true;
|
| 219 |
h += '<div class="md-sec"><div class="md-sec-title">🔄 Lịch sử đối đầu</div>';
|
|
|
|
| 226 |
h += '<span class="md-h2h-away">'+esc(m.away||'')+'</span></span></div>';
|
| 227 |
});
|
| 228 |
h += '</div>';
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
}
|
| 230 |
|
| 231 |
+
// 4. Team form
|
| 232 |
if((data.home_form && data.home_form.length > 0) || (data.away_form && data.away_form.length > 0)){
|
| 233 |
hasAnyContent = true;
|
| 234 |
h += '<div class="md-sec"><div class="md-sec-title">📈 Phong độ gần đây</div>';
|
|
|
|
| 245 |
h += '</div>';
|
| 246 |
}
|
| 247 |
|
| 248 |
+
// 5. Events (substitutions)
|
| 249 |
+
if(data.events && data.events.length > 0){
|
| 250 |
hasAnyContent = true;
|
| 251 |
+
h += '<div class="md-sec"><div class="md-sec-title">🔄 Thay đổi cầu thủ</div>';
|
| 252 |
+
h += '<div class="md-events">';
|
| 253 |
+
data.events.forEach(function(ev){
|
| 254 |
+
const cls = ev.team === 'home' ? 'home' : (ev.team === 'away' ? 'away' : '');
|
| 255 |
+
const time = ev.time || '';
|
| 256 |
+
const players = (ev.players || []).join(' → ');
|
| 257 |
+
h += '<div class="md-event-chip '+cls+'">'+esc(time)+' '+esc(players)+'</div>';
|
| 258 |
+
});
|
| 259 |
+
h += '</div></div>';
|
| 260 |
}
|
| 261 |
|
| 262 |
+
// 6. Timeline/Commentaries (structured)
|
| 263 |
+
if(data.timeline && data.timeline.length > 0){
|
| 264 |
+
hasAnyContent = true;
|
| 265 |
+
h += '<div class="md-sec"><div class="md-sec-title">📢 Diễn biến trận đấu</div>';
|
| 266 |
+
h += '<div class="md-timeline">';
|
| 267 |
+
let lastHalf = '';
|
| 268 |
+
data.timeline.forEach(function(t){
|
| 269 |
+
if(t.half && t.half !== lastHalf){
|
| 270 |
+
h += '<div class="md-tl-half">'+esc(t.half)+'</div>';
|
| 271 |
+
lastHalf = t.half;
|
| 272 |
+
}
|
| 273 |
+
let cls = '';
|
| 274 |
+
const text = t.text || '';
|
| 275 |
+
if(text.includes('⚽') || text.toLowerCase().includes('goal') || text.includes('Bàn')) cls = 'goal';
|
| 276 |
+
if(text.includes('thẻ đỏ') || text.includes('red card')) cls = 'card';
|
| 277 |
+
if(text.includes('thay') || text.includes('↔')) cls = 'sub';
|
| 278 |
+
h += '<div class="md-tl-item '+cls+'">';
|
| 279 |
+
if(t.time) h += '<span class="md-tl-time">'+esc(t.time)+'</span>';
|
| 280 |
+
h += esc(text)+'</div>';
|
| 281 |
+
});
|
| 282 |
+
h += '</div></div>';
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
// 7. Raw HTML fallback sections
|
| 286 |
+
if(data.stats_html && data.stats_html.length > 10 && !data.stats_parsed){
|
| 287 |
hasAnyContent = true;
|
| 288 |
h += '<div class="md-sec"><div class="md-sec-title">🌟 Thông số cầu thủ</div>';
|
| 289 |
h += '<div class="md-raw-html">' + sanitizeHtml(data.stats_html) + '</div></div>';
|
| 290 |
}
|
| 291 |
|
| 292 |
+
if(data.commentaries_html && data.commentaries_html.length > 10 && !data.timeline){
|
|
|
|
| 293 |
hasAnyContent = true;
|
| 294 |
h += '<div class="md-sec"><div class="md-sec-title">📢 Diễn biến</div>';
|
| 295 |
h += '<div class="md-preview-text md-raw-html">' + sanitizeHtml(data.commentaries_html) + '</div></div>';
|
| 296 |
}
|
| 297 |
|
| 298 |
+
if(data.pre_match_html && data.pre_match_html.length > 10 && !info.home_team){
|
| 299 |
+
hasAnyContent = true;
|
| 300 |
+
h += '<div class="md-sec"><div class="md-sec-title">📋 Thông tin trận đấu</div>';
|
| 301 |
+
h += '<div class="md-preview-text md-raw-html">' + sanitizeHtml(data.pre_match_html) + '</div></div>';
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
if(!hasAnyContent){
|
| 305 |
if(data.found === false){
|
| 306 |
+
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: mã trận đấu không đúng hoặc dữ liệu chưa cập nhật.</small></div>';
|
| 307 |
} else {
|
| 308 |
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>';
|
| 309 |
}
|
|
|
|
| 332 |
tabs.insertBefore(d, tabs.firstChild);
|
| 333 |
}
|
| 334 |
|
| 335 |
+
// Capture event_id and URL from match links
|
| 336 |
document.addEventListener('click', function(e){
|
| 337 |
const md = e.target.closest('.match-detail');
|
| 338 |
if(!md) return;
|
| 339 |
+
// Try multiple selectors for match links
|
| 340 |
+
const link = md.querySelector('.status a, .teams a[href*="tran-dau"], a[href*="tran-dau"]');
|
| 341 |
if(link){
|
| 342 |
const href = link.getAttribute('href') || '';
|
| 343 |
const m = href.match(/\/tran-dau\/(\d+)\//);
|