bep40 commited on
Commit
b1a2fd5
·
verified ·
1 Parent(s): 4fdfc66

Rewrite match_detail.js: structured timeline, events, stats, score+status display

Browse files
Files changed (1) hide show
  1. 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:32px;height:32px;object-fit:contain}
17
- .md-pre-team span{font-size:11px;color:#ddd;text-align:center}
18
- .md-pre-vs{font-size:18px;font-weight:900;color:#f0c040;min-width:40px;text-align:center}
 
 
 
 
 
 
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:50px;flex-shrink:0}
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:11px;font-weight:700;color:#aaa;margin-bottom:4px}
45
- .md-no-data{color:#777;font-size:12px;padding:16px;text-align:center}
46
- .md-err{color:#e74c3c;font-size:12px;padding:16px;text-align:center}
47
- .md-warn{color:#f0c040;font-size:12px;padding:12px;text-align:center;background:#1a1a0d;border-radius:8px;border:1px solid #333}
48
- .mo-tab-detail{}
49
- .md-raw-html{overflow:hidden;max-width:100%}
50
- .md-raw-html img{max-width:100%;height:auto;display:block}
51
- .md-raw-html table{max-width:100%;table-layout:fixed;word-break:break-word}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  `;
53
  document.head.appendChild(s);
54
  }
55
 
56
- function esc(s){return String(s||'').replace(/[&<>"']/g,function(m){return{'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]})}
57
 
58
  function sanitizeHtml(html){
59
  if(!html) return '';
60
- return html
61
- .replace(/<script[^>]*>[\s\S]*?<\/script>/gi,'')
62
- .replace(/<iframe[^>]*>[\s\S]*?<\/iframe>/gi,'')
63
- .replace(/<ins[^>]*>[\s\S]*?<\/ins>/gi,'')
64
- .replace(/<style[^>]*>[\s\S]*?<\/style>/gi,'')
65
- .replace(/on\w+\s*=\s*["'][^"']*["']/gi,'')
66
- .replace(/style\s*=\s*["'][^"']*["']/gi,'');
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. Pre-match info card
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">VS</div>';
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- if(info.date || info.time || info.stadium || info.referee){
 
 
 
 
 
 
 
141
  h += '<div class="md-pre-info">';
142
- if(info.date) h += '<span class="md-pre-info-item">📅 <b>'+esc(info.date)+'</b></span>';
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. H2H Stats
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- // 2. H2H matches
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
- // 3. Team form
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
- // 4. Lineups
210
- if(data.lineups_html && data.lineups_html.length > 10){
211
  hasAnyContent = true;
212
- h += '<div class="md-sec"><div class="md-sec-title">📋 Đội hình</div>';
213
- h += '<div class="md-raw-html">' + sanitizeHtml(data.lineups_html) + '</div></div>';
 
 
 
 
 
 
 
214
  }
215
 
216
- // 5. Player performance stats
217
- if(data.stats_html && data.stats_html.length > 10){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- // 6. Commentaries
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(!h){
 
 
 
 
 
 
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 có thể:<br>• 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>';
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 both event_id and full URL from match links
262
  document.addEventListener('click', function(e){
263
  const md = e.target.closest('.match-detail');
264
  if(!md) return;
265
- const link = md.querySelector('.status a, a[href*="tran-dau"]');
 
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: 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+)\//);